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

@samletnorge/feedback-widget

v0.1.1

Published

Universal feedback widget that creates GitHub issues

Readme

💬 Universal Feedback Widget

A lightweight, framework-agnostic feedback widget that automatically creates GitHub discussions or GitHub issues. Works with any website - React, Vue, Svelte, plain HTML, or any other framework.

Feedback Widget Demo NPM Version Bundle Size

✨ Features

  • 🎯 Zero Dependencies - Works without any external libraries
  • 🌐 Universal Compatibility - Works with any framework or vanilla HTML
  • 💬 GitHub Discussions - Creates threaded discussions (recommended)
  • 🐛 GitHub Issues - Creates trackable issues for bugs/features
  • 🎨 Flexible Theming - Inherit page styles, Tailwind CSS, or custom colors
  • 📱 Responsive Design - Mobile-friendly with elegant vertical tab
  • Lightweight - Under 20KB minified and gzipped
  • 🔧 Custom Triggers - Use your own buttons or the default tab
  • 📝 Custom Forms - Bring your own form design
  • 🚀 Programmatic API - Trigger modals via JavaScript
  • ⌨️ Accessibility Ready - Full keyboard navigation and ARIA support

🚀 Quick Start

Option 1: CDN (Recommended)

Add these two lines to your HTML and you're done:

<!-- Include the widget -->
<script src="https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js"></script>

<!-- GitHub Discussions (Recommended) -->
<feedback-widget 
    data-repo="your-username/your-repo" 
    data-token="your-github-token"
    data-type="discussion"
    data-category="feedback"
>
</feedback-widget>

Option 2: NPM Install

npm install @samletnorge/feedback-widget
<script src="node_modules/@samletnorge/feedback-widget/dist/feedback-widget.min.js"></script>
<feedback-widget data-repo="user/repo" data-token="token"></feedback-widget>

💭 Discussions vs Issues

Choose the right GitHub feature for your feedback collection:

| Feature | GitHub Discussions | GitHub Issues | |---------|-------------------|---------------| | Best for | General feedback, questions, community input | Bug reports, feature requests, actionable items | | Structure | Threaded conversations, organized by category | Linear comments, organized by labels | | Workflow | Community-driven discussions | Project management, assignees, milestones | | Configuration | data-type="discussion"data-category="feedback" | data-type="issue"data-labels="feedback,bug" |

⚙️ Configuration Reference

Core Options

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-repo | String | Required | GitHub repository (owner/repo) | | data-token | String | Required | GitHub Personal Access Token | | data-type | String | "discussion" | "discussion" or "issue" | | data-category | String | "feedback" | Discussion category name | | data-labels | String | "feedback" | Comma-separated issue labels | | data-title | String | "Feedback" | Modal title |

Appearance & Behavior

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-position | String | "right" | "right", "left", "bottom-right", "bottom-left" | | data-inherit-styling | Boolean | false | Use page CSS instead of inline styles | | data-custom-trigger | String | null | CSS selector for custom trigger button | | data-custom-form | String | null | CSS selector for custom form | | data-theme | String | "light" | "light" or "dark" |

Theming Options

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | data-primary-color | String | "#007bff" | Primary color (hex/rgb) | | data-background-color | String | "#ffffff" | Modal background color | | data-text-color | String | "#333333" | Text color | | data-border-color | String | "#dddddd" | Border color | | data-border-radius | String | "8px" | Border radius | | data-font-family | String | "system-ui, -apple-system, sans-serif" | Font family |

CSS Class Support

| Attribute | Type | Description | |-----------|------|-------------| | data-primary-color-class | String | CSS class for primary color (e.g., "bg-blue-600") | | data-background-color-class | String | CSS class for background color | | data-text-color-class | String | CSS class for text color | | data-border-color-class | String | CSS class for border color |

🎨 Theming & Customization

1. Inherit Page Styles (Recommended)

Perfect integration with your existing design:

<!-- Enable inherit styling -->
<feedback-widget 
    data-repo="myuser/myrepo"
    data-token="token"
    data-inherit-styling="true"
>
</feedback-widget>

Then add CSS to style the widget components:

/* Regular CSS */
.feedback-widget-modal {
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 0.5rem;
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
  padding: 1.5rem;
}

.feedback-widget-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1.5rem;
}

.feedback-widget-title {
  font-size: 1.25rem;
  font-weight: 600;
  color: #111827;
  margin: 0;
}

.feedback-widget-input,
.feedback-widget-textarea {
  width: 100%;
  padding: 0.75rem;
  border: 1px solid #d1d5db;
  border-radius: 0.375rem;
  background: white;
}

.feedback-widget-btn-primary {
  background: #3b82f6;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  cursor: pointer;
}

2. Tailwind CSS Classes

<feedback-widget 
    data-primary-color-class="bg-blue-600"
    data-background-color-class="bg-white dark:bg-gray-900"
    data-text-color-class="text-gray-900 dark:text-white"
    data-border-color-class="border-gray-200 dark:border-gray-700"
>
</feedback-widget>

For Svelte/Vue with Tailwind:

<style>
/* Tailwind CSS classes */
:global(.feedback-widget-modal) {
  @apply bg-white border border-gray-200 rounded-lg shadow-lg p-6;
}

:global(.feedback-widget-header) {
  @apply flex justify-between items-center mb-6;
}

:global(.feedback-widget-title) {
  @apply text-xl font-semibold text-gray-900 m-0;
}

:global(.feedback-widget-input),
:global(.feedback-widget-textarea) {
  @apply w-full p-3 border border-gray-300 rounded-md bg-white text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500;
}

:global(.feedback-widget-btn-primary) {
  @apply bg-blue-600 text-white hover:bg-blue-700;
}

:global(.feedback-widget-btn-secondary) {
  @apply bg-gray-100 text-gray-700 hover:bg-gray-200;
}
</style>

3. Custom Colors & Positioning

<feedback-widget 
    data-primary-color="#8B5CF6"
    data-background-color="#F8FAFC"
    data-text-color="#1E293B"
    data-border-radius="12px"
    data-position="left"
    data-theme="dark"
>
</feedback-widget>

Available CSS Classes for Inherit Styling

| CSS Class | Element | Description | |-----------|---------|-------------| | .feedback-widget-modal | Modal container | Main modal wrapper | | .feedback-widget-header | Header section | Contains title and close button | | .feedback-widget-title | Modal title | H2 heading element | | .feedback-widget-close-btn | Close button | X button in header | | .feedback-widget-form | Form element | Main form container | | .feedback-widget-field | Field wrapper | Contains label + input | | .feedback-widget-label | Form labels | Label elements | | .feedback-widget-input | Text inputs | Subject and email inputs | | .feedback-widget-textarea | Textarea | Feedback message field | | .feedback-widget-actions | Button container | Cancel and submit buttons | | .feedback-widget-btn | Base button | Base class for all buttons | | .feedback-widget-btn-primary | Submit button | Primary action button | | .feedback-widget-btn-secondary | Cancel button | Secondary action button |

🎯 Custom Triggers & Forms

Custom Trigger Button

Use your own styled button instead of the default vertical tab:

<button id="my-feedback-btn">Give Feedback</button>

<feedback-widget 
    data-repo="myuser/myrepo"
    data-token="token"
    data-custom-trigger="#my-feedback-btn"
>
</feedback-widget>

Programmatic API

// Simple helper function
window.openFeedbackWidget();

// Or dispatch custom event
document.dispatchEvent(new Event('feedback-widget-open'));

// Access widget directly
const widget = document.querySelector('feedback-widget')._feedbackWidgetInstance;
widget.openModal();

Custom Form

Provide your own completely custom form:

<div id="custom-form" style="display: none;">
    <form>
        <input name="subject" placeholder="What's this about?" required>
        <textarea name="feedback" placeholder="Tell us more..." required></textarea>
        <input name="email" placeholder="Email (optional)">
        <button type="submit">Send</button>
    </form>
</div>

<feedback-widget 
    data-custom-form="#custom-form"
    data-repo="myuser/myrepo"
    data-token="token"
>
</feedback-widget>

🔑 GitHub Token Setup

For GitHub Discussions

  1. Go to GitHub Settings > Developer settings > Personal access tokens
  2. Click "Generate new token (classic)"
  3. Give it a name like "Feedback Widget"
  4. Select scopes:
    • write:discussion (required for discussions) !! NB: This is why this is recommended. It only allows writing discussions, not whole repo
  5. Click "Generate token"
  6. Copy the token (starts with github_pat_ or ghp_)

For GitHub Issues

Same steps as above, but you only need:

  • public_repo (for public repositories) or repo (for private repos) for these i would recomend using a github finetuned token with only the issues scope. but you need to be admin for that.

⚠️ Security Note: Never expose your token in client-side code for production. Consider using a serverless function or GitHub App for production deployments. THIS IS ALSO WHY I RECOMMEND USING TOKEN WITH ONLY write:discussion SCOPE or issues SCOPE.

🌟 Framework Examples

React/Next.js

import { useEffect } from 'react';

function App() {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js';
    document.head.appendChild(script);
  }, []);

  return (
    <div>
      <h1>My React App</h1>
      <feedback-widget 
        data-repo="myuser/myrepo"
        data-token={process.env.REACT_APP_GITHUB_TOKEN}
        data-type="discussion"
        data-inherit-styling="true"
      />
    </div>
  );
}

Vue.js

<template>
  <div>
    <h1>My Vue App</h1>
    <feedback-widget 
      :data-repo="repo"
      :data-token="token"
      data-type="discussion"
      data-category="general"
      data-position="left"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      repo: "myuser/myrepo",
      token: process.env.VUE_APP_GITHUB_TOKEN
    }
  },
  mounted() {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js';
    document.head.appendChild(script);
  }
}
</script>

Svelte/SvelteKit

<script>
  import { onMount } from 'svelte';
  
  onMount(() => {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js';
    document.head.appendChild(script);
  });
</script>

<!-- CSS for custom styling -->
<style>
  :global(.feedback-widget-modal) {
    @apply bg-background border border-border rounded-lg;
  }
</style>

<h1>My Svelte App</h1>
<feedback-widget 
  data-repo="myuser/myrepo"
  data-token="token"
  data-primary-color-class="bg-primary"
  data-background-color-class="bg-background"
  data-inherit-styling="true"
/>

Angular

// app.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <feedback-widget 
      [attr.data-repo]="repo"
      [attr.data-token]="token"
      data-type="discussion"
      data-inherit-styling="true">
    </feedback-widget>
  `
})
export class AppComponent implements OnInit {
  repo = 'myuser/myrepo';
  token = environment.githubToken;

  ngOnInit() {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js';
    document.head.appendChild(script);
  }
}

Plain HTML

<!DOCTYPE html>
<html>
<head>
    <title>My Website</title>
</head>
<body>
    <h1>Welcome to my website!</h1>
    
    <script src="https://cdn.jsdelivr.net/npm/@samletnorge/feedback-widget@latest/dist/feedback-widget.min.js"></script>
    <feedback-widget 
        data-repo="myuser/myrepo"
        data-token="github_pat_xxxxx"
        data-type="discussion"
        data-category="feedback"
    ></feedback-widget>
</body>
</html>

📊 Generated Content

GitHub Discussions

Discussions are created with this format:

**Subject:** Great new feature idea

Hi! I love the new design, but I think it could use more contrast on the buttons for better accessibility.

---
Contact: [email protected]

---
Submitted via feedback widget
Page: https://mywebsite.com/dashboard
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...

GitHub Issues

Issues are created with this format:

**Subject:** Bug report - Login not working

The login form doesn't submit when I click the button. I've tried multiple browsers.

---
Contact: [email protected]

---
Submitted via feedback widget
Page: https://mywebsite.com/login
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...

🎯 Event Handling

Listen for feedback submissions:

document.addEventListener('feedback-submitted', (event) => {
    console.log('Feedback received:', event.detail);
    // { subject: "Bug report", feedback: "Login broken", email: "[email protected]" }
    
    // Send to your analytics
    gtag('event', 'feedback_submitted', {
        'feedback_type': event.detail.subject ? 'detailed' : 'simple',
        'has_email': !!event.detail.email
    });
});

document.addEventListener('feedback-error', (event) => {
    console.error('Feedback submission failed:', event.detail.error);
});

document.addEventListener('feedback-success', (event) => {
    console.log('Feedback submitted successfully:', event.detail);
});

🛠️ Development

Local Development

git clone https://github.com/samletnorge/feedback-widget.git
cd feedback-widget
pnpm install
pnpm run dev

Visit http://localhost:8080 to see the demo.

Build

pnpm run build  # Creates dist/feedback-widget.min.js

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Add a changelog when you do something significant (using changesets): npx changeset
    • Follow the prompts to describe your changes
    • This will create a new file in the .changeset directory
    • You can add multiple changesets before committing
    • Include the .changeset/ files in your commit
  4. Commit changes: git commit -m 'Add amazing feature'
  5. Push to branch: git push origin feature/amazing-feature
  6. Open a Pull Request
  7. Wait for review and address any feedback
  8. Once approved, your changes will be merged and included in the next release!

📝 License

MIT License - see LICENSE file for details.

🆘 Support

🌟 Show Your Support

If this widget helped your project, please give it a ⭐ on GitHub!


Made with ❤️ for the web community