Widget documentation
Everything you need to install and configure the SaySift widget.
Quick start
Add the script before the closing </body> tag on any page. Replace YOUR_PUBLIC_KEY with a public API key from your project setup page.
<script
src="https://saysift.com/widget/feedback-widget.js"
data-project-key="YOUR_PUBLIC_KEY"
data-locale="auto"
></script>
The script adds a compact feedback tab at the edge of the page. No build step required — it works with any frontend framework or plain HTML.
Configuration
All configuration is done via data-* attributes on the <script> tag.
| Attribute | Values | Default | Description |
|---|---|---|---|
data-project-key |
string | required | Your public API key. The widget will not load without it. |
data-locale |
auto · en · tr |
auto |
Widget language. auto detects from html[lang] or navigator.language, falling back to English. |
data-launcher |
tab · inline |
tab |
Launch mode. tab renders a fixed button at the screen edge. inline suppresses the button — open the widget from your own UI instead. See Inline launcher. |
data-position |
right · left |
from project settings | Which edge the tab launcher appears on. Overrides the project-level setting. |
data-edge-offset |
0–240 (px) | 0 |
Distance from the left or right edge. Useful when the launcher would overlap a scrollbar or navigation. |
data-bottom-offset |
0–240 (px) | vertically centered | Distance from the bottom edge. When omitted, the launcher is centered vertically. |
data-saysift-theme |
auto · light · dark |
auto |
Widget color scheme. auto follows html[data-saysift-theme], html[data-bs-theme], or prefers-color-scheme. |
data-branding |
hidden |
shown | Set to hidden to remove the "Powered by SaySift" mark from the widget footer. |
data-container |
CSS selector | body |
DOM element the widget mounts into. Falls back to body if the selector is invalid or matches nothing. |
data-technical-details |
any · false |
enabled | Set to false to prevent the widget from sending page URL, browser, device, language, and screen size with each submission. |
data-include-url-details |
true |
origin + path only | Set to true to send the full URL including query string and hash. By default only the origin and pathname are sent. |
data-csp-nonce |
string | "" |
CSP nonce applied to the widget's inline <style>. Falls back to the script element's own nonce attribute if omitted. |
JavaScript API
The widget exposes a global window.SaySift object once the script loads.
open(options)
Open the feedback modal. All options are optional.
SaySift.open({
type: "bug", // pre-select: "bug" | "idea" | "question" | "general"
title: "Checkout bug", // pre-fill the title field (max 200 chars)
context: { // extra metadata sent with the submission
area: "checkout", // allowed keys: area, feature, source
feature: "payment"
},
theme: "dark", // override theme: "light" | "dark" | "auto"
instanceId: "fh-…" // target a specific instance (multi-widget setups)
});
close(options)
Close the modal. Prompts the user to confirm if there is unsaved input.
configure(options)
Apply options (same shape as open()) without opening the modal.
identify(options)
Attach a user identity to all submissions during this browser session. See User identity for details.
resetIdentity()
Clear the stored identity from sessionStorage.
Events
The widget dispatches CustomEvent instances on document. Use these to track submissions or react to widget state.
document.addEventListener("saysift:submitted", (event) => {
console.log(event.detail); // { instanceId, projectKey, id }
});
| Event | Fired when | detail properties |
|---|---|---|
saysift:loaded |
Widget DOM is rendered and ready. | instanceId, projectKey |
saysift:opened |
Modal opens (via launcher click or SaySift.open()). |
instanceId, projectKey |
saysift:submit-started |
Validation passed and the network request begins. | instanceId, projectKey |
saysift:submitted |
Feedback or survey submitted successfully. | instanceId, projectKey, id (feedback ID), surveyId (survey submissions only) |
saysift:failed |
Config load or submission fails. | instanceId, projectKey, reason — one of: config, timeout, network, origin_denied, duplicate, validation_error, project_inactive, administratively_suspended, or http_[status] |
Inline launcher
Use your own button or link to open the widget instead of the default edge tab.
<script
src="https://saysift.com/widget/feedback-widget.js"
data-project-key="YOUR_PUBLIC_KEY"
data-launcher="inline"
></script>
<!-- Any element with data-saysift-open opens the widget -->
<button type="button" data-saysift-open>Send feedback</button>
With data-launcher="inline" the edge tab is not rendered. Any element with the data-saysift-open attribute will open the modal on click.
If you have multiple widget instances on the same page, add data-saysift-instance="[instanceId]" to the trigger element to target a specific one. The instance ID is available in event detail.instanceId after saysift:loaded fires.
User identity
Attach a user ID and traits to feedback submissions so you can correlate reports with accounts in your product.
SaySift.identify({
externalId: "user_123", // required; must not contain "@"
traits: {
name: "Alice",
plan: "pro",
role: "admin",
company: "Acme",
version: "3.1.0",
environment: "production"
}
});
Call identify() after your app has authenticated the user — typically on page load or after login. The identity is stored in sessionStorage and automatically included with every feedback submission until the session ends or resetIdentity() is called.
| Field | Type | Notes |
|---|---|---|
externalId |
string | Required. Your internal user ID. Must not contain @. Truncated to 200 chars. |
traits.name |
string | Display name. |
traits.plan |
string | Subscription or pricing tier. |
traits.role |
string | User role within your product. |
traits.company |
string | Organization name. |
traits.version |
string | App version the user is running. |
traits.environment |
string | e.g. production, staging. |
Unknown trait keys are silently ignored. All values are coerced to strings and truncated to 200 characters each.