// documentation

Widget Configuration

Everything you need to embed and configure the feedback widget on your site.

Quick Start

Add the widget to any website with a single script tag. The widget is fully self-contained — it loads in a Shadow DOM, so it won't interfere with your page's styles or JavaScript.

html
<script
  src="https://cdn.feedsnap.dev/widget.js"
  data-project-key="YOUR_PROJECT_API_KEY"
  data-telemetry="true"
  async
></script>

That's it. The widget button will appear in the bottom-right corner. Users can click it to submit feedback with an optional screenshot.

Find your project API key in Projects → select your project → copy the API Key from the project card.

Script Tag Attributes

The simplest way to configure the widget is through data- attributes on the script tag. No JavaScript required — just set the attributes and the widget auto-initializes when the script loads.

html
<script
  src="https://cdn.feedsnap.dev/widget.js"
  data-project-key="YOUR_PROJECT_API_KEY"
  data-position="bottom-left"
  data-color="#e11d48"
  data-label="Report Bug"
  data-telemetry="true"
  data-framework-detection="true"
  data-replay="true"
  async
></script>

Available Attributes

Property Type Default Description
data-project-key string (required) Maps to projectToken
data-position string 'bottom-right' Maps to position
data-color string '#5B5FC7' Maps to primaryColor
data-label string 'Feedback' Maps to buttonLabel
data-api-url string 'https://feedsnap.dev' Maps to apiUrl. Override only if self-hosting.
data-telemetry 'true' | 'false' not set Maps to enableTelemetry. Defaults to off if attribute is absent.
data-framework-detection 'true' | 'false' not set Maps to enableFrameworkDetection
data-replay 'true' | 'false' not set Maps to enableReplay
Boolean attributes use string values "true" or "false". Omitting the attribute entirely means the feature stays off.

JavaScript API

For more control, initialize the widget via JavaScript. This is useful when you need to set the config dynamically or when you want to call the metadata API.

Queue-Based Init (Recommended)

This pattern works even if the widget script hasn't loaded yet. Commands are queued and executed once the script loads — the same pattern used by Google Analytics and Segment.

html
<!-- Initialize the queue before the script loads -->
<script>
  window.FeedbackWidget = window.FeedbackWidget || [];
  window.FeedbackWidget.push(['init', {
    projectToken: 'YOUR_PROJECT_API_KEY',
    position: 'bottom-right',
    primaryColor: '#5B5FC7',
    enableTelemetry: true,
    enableReplay: true,
  }]);
</script>

<!-- Load the widget (async, non-blocking) -->
<script src="https://cdn.feedsnap.dev/widget.js" async></script>

Direct Init (After Script Load)

If you load the script first and then initialize, use the direct API:

javascript
// Only works after widget.js has loaded
window.FeedbackWidget.init({
  projectToken: 'YOUR_PROJECT_API_KEY',
  position: 'bottom-left',
  primaryColor: '#16a34a',
  buttonLabel: 'Help',
  enableTelemetry: true,
});
The widget can only be initialized once. Calling init() a second time will log a warning and be ignored.

Configuration Reference

Complete list of all configuration properties accepted by the FeedbackConfig object.

Property Type Default Description
projectToken string (required) Your project API key. Found in Projects > your project > API Key.
apiUrl string 'https://feedsnap.dev' Base URL for the feedback API. Override only if self-hosting.
position 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' 'bottom-right' Corner where the feedback button appears.
primaryColor string '#5B5FC7' Brand color used for the button, accents, and active states. Any valid CSS color.
buttonLabel string 'Feedback' Text shown on the floating feedback button.
enableTelemetry boolean false Capture console logs, network requests, JS errors, and user actions automatically.
enableFrameworkDetection boolean false Detect the frontend framework (React, Vue, Angular, Svelte) and capture the component tree.
enableReplay boolean false Record a rolling ~30-second session replay using rrweb. Lazy-loads a separate bundle.

Appearance

Customize the widget's position and branding to match your site.

Position

The position property controls which corner the floating button appears in. The modal always opens adjacent to the button.

bottom-right
bottom-left
top-right
top-left

Brand Color

Set primaryColor to match your brand. Used for the button background, accent highlights, and active states.

javascript
// Hex color
{ primaryColor: '#e11d48' }

// Named color
{ primaryColor: 'rebeccapurple' }

// HSL
{ primaryColor: 'hsl(262, 83%, 58%)' }

Button Label

Customize the text on the floating button. Keep it short — 1-2 words works best.

javascript
{ buttonLabel: 'Report Bug' }
{ buttonLabel: 'Help' }
{ buttonLabel: 'Feedback' }  // default

CSS Isolation

The widget renders inside a Shadow DOM, which means your page's CSS won't affect the widget, and the widget's CSS won't leak into your page. No special configuration needed — this happens automatically.

Telemetry

When enableTelemetry: true, the widget automatically captures browser context alongside every feedback submission — a complete debugging snapshot.

What Gets Captured

Console Logs Last 100 console.log/warn/error/info/debug entries with args
Network Requests Last 50 fetch/XHR calls with status, duration, headers, and response body
JavaScript Errors Last 50 uncaught errors and unhandled promise rejections with stack traces
User Actions Last 50 clicks, keyboard events (Enter, Escape, Tab), and SPA navigations
Environment URL, viewport, screen size, browser, OS, language, timezone

Buffer Sizes

Telemetry uses rolling buffers so memory stays bounded. Once a buffer fills, the oldest entries are dropped:

Property Type Default Description
consoleBufferSize number 100 Max console entries retained
networkBufferSize number 50 Max network requests retained
errorBufferSize number 50 Max JS errors retained
actionBufferSize number 50 Max user actions retained

Network Request Filtering

The widget automatically filters out its own API requests from the network telemetry, preventing noise in the captured data.

Framework Detection

When enableFrameworkDetection: true, the widget detects the frontend framework and captures the component tree at submission time.

React Fiber tree extraction + Next.js
Vue __vue_app__ inspection + Nuxt
Angular ng.getComponent v12+
Svelte __svelte_meta

Session Replay

When enableReplay: true, the widget loads a separate bundle (widget-replay.js, ~35KB gzipped) that uses rrweb to record a rolling ~30-second buffer of DOM changes.

javascript
{
  enableReplay: true,
  // The replay bundle is lazy-loaded from:
  // {cdnUrl}/widget-replay.js
}

How It Works

1. The recorder starts on widget init and captures DOM mutations continuously
2. Full DOM snapshots are taken every 15 seconds, creating self-contained segments
3. Only the most recent 3 segments are kept (~30-45 seconds of replay)
4. When the feedback modal opens, the recorder stops and events are frozen
5. On submit, events are gzip-compressed in the browser and uploaded as a binary blob

Privacy Defaults

Session replay ships with conservative privacy settings:

All input values are masked (replaced with ***)
Mouse movements are not tracked
Scripts, comments, and meta tags are stripped from the DOM snapshot
Canvas elements, images, and fonts are not inlined
No cookie or localStorage data is captured

Custom Metadata

Attach arbitrary key-value data to every feedback submission. Useful for user context, feature flags, cart state, or any app-specific debugging data.

javascript
// Set metadata at any point — persists until page unloads
window.FeedbackWidget.metadata({
  userId: 'usr_42',
  email: '[email protected]',
  plan: 'pro',
  role: 'admin',
});

// Subsequent calls merge (not replace) with existing data
window.FeedbackWidget.metadata({
  cartTotal: 149.99,
  cartItems: 3,
  currency: 'USD',
});

// Result: all 7 keys included in the next feedback submission

Common Patterns

javascript
// After user login
function onLogin(user) {
  window.FeedbackWidget.metadata({
    userId: user.id,
    email: user.email,
    plan: user.subscription.plan,
  });
}

// On page navigation (SPA)
function onRouteChange(route) {
  window.FeedbackWidget.metadata({
    currentPage: route.name,
    pageParams: route.params,
  });
}

// Feature flags
window.FeedbackWidget.metadata({
  featureFlags: {
    darkMode: true,
    betaCheckout: false,
    newSearch: true,
  },
});
Custom metadata appears in the Info tab of the feedback detail view under "Custom Metadata" with JSON syntax highlighting.

Screenshots

Screenshots are captured via the native Canvas API. When a user clicks the screenshot button, the page is captured and presented in an annotation editor.

Annotation Tools

Draw Freehand pen with adjustable color and stroke width
Arrow Draw arrows to point at specific elements
Text Add text labels anywhere on the screenshot
Blur Redact sensitive areas (PII, passwords, etc.)

Image Upload

Users can also upload an image instead of taking a live screenshot. The uploaded image goes through the same annotation editor.

Storage

Screenshots are uploaded as PNG blobs to a content-addressable store. The server returns a SHA-256 hash. Duplicate screenshots are automatically deduplicated.

Privacy & Security

Shadow DOM Isolation

The widget renders inside a Shadow DOM for complete CSS and event isolation. Host-page modal libraries (Radix UI, Headless UI) won't close when users click the widget button.

Sensitive Data Redaction

The framework detection component tree redacts props matching sensitive patterns:

text
Redacted prop names (case-insensitive):
  password, token, secret, key, auth, credential,
  ssn, credit, card, cvv, expir

Example: apiToken="sk_live_abc123" → apiToken="[REDACTED]"

Authentication

The widget uses the project token as a Bearer token. This is a write-only token — it can only create feedback and upload media. It cannot read, update, or delete data.

text
API Endpoints (project-token authenticated):
  POST /api/widget/{token}/feedback    Submit feedback
  POST /api/widget/{token}/snapshot    Upload screenshot PNG
  POST /api/widget/{token}/replay      Upload replay data