npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

featurely-feature-reporter

v1.0.22

Published

Embeddable widget for collecting user feature requests and bug reports with Featurely

Readme

featurely-feature-reporter

Embeddable widget for collecting user feature requests and bug reports with Featurely.

📦 Installation

npm install featurely-feature-reporter

🚀 Quick Start

import { FeatureReporter } from "featurely-feature-reporter";

const reporter = new FeatureReporter({
  apiKey: "your-featurely-api-key",
  projectId: "your-project-id",
  position: "bottom-right",
  color: "blue",
  icon: "comment",
});

reporter.init();

✨ Features

  • 💡 Feature Requests - Let users submit feature ideas
  • 🐛 Bug Reports - Collect bug reports with screenshots
  • 📸 Screenshot Capture - Automatic or manual screenshot capture
  • 🎨 Customizable - Colors, icons, and positioning
  • 🎯 Draggable Widget - Optional drag-to-reposition functionality
  • 📱 Mobile Responsive - Works great on all devices
  • 🔒 User Context - Track submissions by user
  • 🔗 Dashboard Link - Optional link to your public roadmap
  • 🎭 TypeScript - Fully typed with comprehensive definitions

📖 Usage

Basic Setup

import { FeatureReporter } from "featurely-feature-reporter";

// Initialize the widget
const reporter = new FeatureReporter({
  apiKey: "ft_live_your_api_key",
  projectId: "proj_123",
});

// Render the widget
reporter.init();

With User Context

const reporter = new FeatureReporter({
  apiKey: "ft_live_your_api_key",
  projectId: "proj_123",
  user: {
    id: "user_123",
    email: "[email protected]",
    name: "John Doe",
  },
});

reporter.init();

// Update user context later (e.g., after login)
reporter.setUser("user_456", "[email protected]", "Jane Smith");

Full Customization

const reporter = new FeatureReporter({
  apiKey: "ft_live_your_api_key",
  projectId: "proj_123",

  // Position
  position: "bottom-right", // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'right-center' | 'left-center'

  // Appearance
  color: "purple", // 'blue' | 'green' | 'purple' | 'red' | 'orange' | 'pink'
  icon: "lightbulb", // 'comment' | 'lightbulb' | 'bug' | 'message' | 'plus' | 'star'

  // Features
  enableFeatureRequests: true,
  enableBugReports: true,
  draggable: true,
  showDashboardLink: true,

  // Custom labels (all fields optional — see full list below)
  labels: {
    widgetTooltip: "Send feedback",
    modalTitle: "Send Feedback",
    featureRequestTitle: "💡 Request a Feature",
    bugReportTitle: "🐛 Report a Bug",
    featureTitleLabel: "Feature title",
    bugTitleLabel: "Short description",
    descriptionLabel: "Description",
    featureTitlePlaceholder: "What would you like to see?",
    bugTitlePlaceholder: "What went wrong?",
    featureDescriptionPlaceholder: "Describe the feature and why it would be useful...",
    bugDescriptionPlaceholder: "What happened? What did you expect to happen?",
    emailLabel: "Email (optional)",
    emailPlaceholder: "[email protected]",
    emailHint: "Get notified when we ship this",
    submitButton: "Submit",
    cancelButton: "Cancel",
    submittingText: "Submitting...",
    featureSuccessMessage: "✅ Feature request submitted! Thanks for your feedback.",
    bugSuccessMessage: "✅ Bug report submitted! We'll look into it.",
    featureErrorMessage: "❌ Could not submit feature request. Please try again.",
    bugErrorMessage: "❌ Could not submit bug report. Please try again.",
  },

  // Callbacks
  onSubmit: (type, data) => {
    console.log(`${type} submitted:`, data);
  },
  onError: (error) => {
    console.error("Submission error:", error);
  },
});

reporter.init();

React Integration

import { useEffect, useRef } from "react";
import { FeatureReporter } from "featurely-feature-reporter";

function App() {
  const reporterRef = useRef<FeatureReporter | null>(null);

  useEffect(() => {
    reporterRef.current = new FeatureReporter({
      apiKey: process.env.REACT_APP_FEATURELY_API_KEY!,
      projectId: process.env.REACT_APP_FEATURELY_PROJECT_ID!,
      position: "bottom-right",
      color: "blue",
    });

    reporterRef.current.init();

    return () => {
      reporterRef.current?.destroy();
    };
  }, []);

  return <div>Your app content</div>;
}

Next.js Integration

// app/layout.tsx or pages/_app.tsx
"use client";

import { useEffect } from "react";
import { FeatureReporter } from "featurely-feature-reporter";

export default function RootLayout({ children }) {
  useEffect(() => {
    if (typeof window !== "undefined") {
      const reporter = new FeatureReporter({
        apiKey: process.env.NEXT_PUBLIC_FEATURELY_API_KEY!,
        projectId: process.env.NEXT_PUBLIC_FEATURELY_PROJECT_ID!,
      });

      reporter.init();

      return () => reporter.destroy();
    }
  }, []);

  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

Vue Integration

<script setup>
import { onMounted, onUnmounted } from "vue";
import { FeatureReporter } from "featurely-feature-reporter";

let reporter = null;

onMounted(() => {
  reporter = new FeatureReporter({
    apiKey: import.meta.env.VITE_FEATURELY_API_KEY,
    projectId: import.meta.env.VITE_FEATURELY_PROJECT_ID,
  });

  reporter.init();
});

onUnmounted(() => {
  reporter?.destroy();
});
</script>

🔧 Configuration

FeatureReporterConfig

| Option | Type | Required | Default | Description | | ----------------------- | ---------------- | -------- | ------------------------ | ------------------------------------ | | apiKey | string | ✅ | - | Your Featurely API key | | projectId | string | ✅ | - | Your Featurely project ID | | apiUrl | string | ❌ | 'https://featurely.no' | Custom API endpoint | | position | WidgetPosition | ❌ | 'bottom-right' | Widget position | | color | WidgetColor | ❌ | 'blue' | Widget color theme | | icon | WidgetIcon | ❌ | 'comment' | Widget icon | | draggable | boolean | ❌ | false | Allow widget dragging | | enableFeatureRequests | boolean | ❌ | true | Enable feature requests | | enableBugReports | boolean | ❌ | true | Enable bug reports | | showDashboardLink | boolean | ❌ | false | Show dashboard link | | user | UserInfo | ❌ | - | User context | | labels | Labels | ❌ | - | Custom text labels (see table below) | | onSubmit | function | ❌ | - | Submission callback | | onError | function | ❌ | - | Error callback |

Position Options

  • 'bottom-right' - Bottom right corner (default)
  • 'bottom-left' - Bottom left corner
  • 'top-right' - Top right corner
  • 'top-left' - Top left corner
  • 'right-center' - Right side, vertically centered
  • 'left-center' - Left side, vertically centered

Color Options

  • 'blue' (default)
  • 'green'
  • 'purple'
  • 'red'
  • 'orange'
  • 'pink'

Icon Options

  • 'comment' (default) - Chat bubble
  • 'lightbulb' - Light bulb
  • 'bug' - Bug icon
  • 'message' - Message icon
  • 'plus' - Plus sign
  • 'star' - Star icon

Labels Options

All labels are optional. Any label not provided falls back to the English default.

| Label key | Default (English) | Description | | ------------------------------- | ----------------------------------------------------------- | ----------------------------------------- | | widgetTooltip | "Send feedback" | Tooltip shown on the floating button | | modalTitle | "Send Feedback" | Title bar of the modal dialog | | featureRequestTitle | "💡 Request a Feature" | Tab/heading for feature requests | | bugReportTitle | "🐛 Report a Bug" | Tab/heading for bug reports | | featureTitleLabel | "Feature title" | Label for the feature title input | | bugTitleLabel | "Short description" | Label for the bug title input | | descriptionLabel | "Description" | Label for the description textarea | | featureTitlePlaceholder | "What would you like to see?" | Placeholder for feature title | | bugTitlePlaceholder | "What went wrong?" | Placeholder for bug title | | featureDescriptionPlaceholder | "Describe the feature and why it would be useful..." | Placeholder for feature description | | bugDescriptionPlaceholder | "What happened? What did you expect to happen?" | Placeholder for bug description | | emailLabel | "Email (optional)" | Label for the email input | | emailPlaceholder | "[email protected]" | Placeholder for email input | | emailHint | "Get notified when we ship this" | Hint text below email input | | submitButton | "Submit" | Submit button label | | cancelButton | "Cancel" | Cancel/close button label | | submittingText | "Submitting..." | Button label while submitting | | featureSuccessMessage | "✅ Feature request submitted! Thanks for your feedback." | Toast after successful feature submission | | bugSuccessMessage | "✅ Bug report submitted! We'll look into it." | Toast after successful bug submission | | featureErrorMessage | "❌ Could not submit feature request. Please try again." | Toast when feature submission fails | | bugErrorMessage | "❌ Could not submit bug report. Please try again." | Toast when bug submission fails |

🔒 Content Security Policy (CSP)

If your site uses a Content-Security-Policy header, you must allow connections to https://www.featurely.no. Without this, form submissions will fail silently.

Add to your connect-src directive:

connect-src 'self' https://www.featurely.no;

Next.js example (next.config.js):

const cspHeader = `
  connect-src 'self' https://www.featurely.no;
`.trim();

If the SDK is blocked by CSP, you will see this in the browser console:

[FeatureReporter] ⚠️ Network error — this may be caused by a Content-Security-Policy.
Add "https://www.featurely.no" to your connect-src directive.

⚠️ Error Messages

The widget logs actionable errors to the browser console:

| Situation | Console message | | ------------------------ | ------------------------------------------------------------- | | Invalid API key (401) | Link to API Keys settings page | | Missing permission (403) | Reminder to add features:write and bugs:write permissions | | CSP blocking request | Instruction to add connect-src https://www.featurely.no |

📸 Screenshot Capture

The widget includes built-in screenshot capability for bug reports:

Automatic Capture (Recommended)

Install html2canvas for automatic full-page screenshots:

npm install html2canvas
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js"></script>

When html2canvas is available, the widget will automatically capture full-page screenshots when users click "Capture Screenshot".

Manual Upload Fallback

If html2canvas is not available, the widget will fall back to a file upload input, allowing users to manually select screenshot files.

🎯 API Methods

init()

Initialize and render the widget on the page.

reporter.init();

setUser(userId, email?, name?)

Update user context for submissions.

reporter.setUser("user_123", "[email protected]", "John Doe");

show()

Show the widget if it was hidden.

reporter.show();

hide()

Hide the widget.

reporter.hide();

destroy()

Remove the widget from the page and clean up.

reporter.destroy();

🌐 Environment Variables

Store your API credentials securely:

React/Vite:

VITE_FEATURELY_API_KEY=ft_live_your_key
VITE_FEATURELY_PROJECT_ID=proj_123

Next.js:

NEXT_PUBLIC_FEATURELY_API_KEY=ft_live_your_key
NEXT_PUBLIC_FEATURELY_PROJECT_ID=proj_123

Vanilla JS:

const reporter = new FeatureReporter({
  apiKey: window.FEATURELY_API_KEY,
  projectId: window.FEATURELY_PROJECT_ID,
});

🎨 Custom Styling

The widget injects minimal styles with high specificity. You can override them:

/* Customize widget button */
.featurely-widget-button {
  width: 64px !important;
  height: 64px !important;
}

/* Customize modal */
.featurely-modal-content {
  border-radius: 16px !important;
}

All styles are prefixed with featurely- to avoid conflicts.

🔒 Privacy & Security

  • Screenshots are captured client-side and sent directly to your Featurely project
  • No third-party services are used for screenshots
  • User data is only sent to your Featurely project
  • API keys should be stored in environment variables
  • All submissions are sent over HTTPS

📱 Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile)

Screenshot capture requires:

  • html2canvas for automatic capture
  • FileReader API for manual upload (universally supported)

🤝 Contributing

Contributions are welcome! Please open an issue or PR.

📄 License

MIT License - see LICENSE file for details.

🔗 Links

💬 Support

For issues or questions: