// 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.
<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.
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.
<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 |
"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.
<!-- 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:
// Only works after widget.js has loaded
window.FeedbackWidget.init({
projectToken: 'YOUR_PROJECT_API_KEY',
position: 'bottom-left',
primaryColor: '#16a34a',
buttonLabel: 'Help',
enableTelemetry: true,
});
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.
// 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.
{ 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
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.
{
enableReplay: true,
// The replay bundle is lazy-loaded from:
// {cdnUrl}/widget-replay.js
}
How It Works
Privacy Defaults
Session replay ships with conservative privacy settings:
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.
// 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
// 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,
},
});
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
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:
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.
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