Skip to content

samletnorge/feedback-widget

Repository files navigation

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: user@example.com

---
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: user@example.com

---
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: "user@example.com" }
    
    // 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.

Show Your Support

If this widget helped your project, please give it a FEEDBACK!


Made with ❤️ for the web community