@obvi/feedback-sdk
v1.2.0
Published
Embeddable browser feedback widget — drop into any web app to collect user feedback powered by Obvious
Readme
Obvious Feedback SDK
Embeddable browser widget that lets users submit feedback from any web app. Feedback is routed to Obvious where it is triaged, tracked, and optionally auto-fixed by Autobuild.
Install
npm / yarn / pnpm / bun
npm install @obvi/feedback-sdkScript tag (CDN)
<script
src="https://cdn.jsdelivr.net/npm/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
></script>Or via unpkg:
<script
src="https://unpkg.com/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
></script>The script auto-initializes the widget when data-pub-key is present.
Quick start
ES module
import { ObviousFeedback } from "@obvi/feedback-sdk";
const widget = ObviousFeedback.init({
publicKey: "fsk_pub_...",
});
// Later: widget.destroy()Script tag with options
<script
src="https://cdn.jsdelivr.net/npm/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
data-theme="dark"
data-env="staging"
></script>Configuration
Pass options to ObviousFeedback.init() or use data-* attributes on the script tag.
| Option | data-* attribute | Type | Default | Description |
| --- | --- | --- | --- | --- |
| publicKey | data-pub-key | string | Required | Your workspace feedback key. |
| apiBaseUrl | data-api-base-url | string | https://api.app.obvious.ai | Base URL for the Obvious API. |
| identityToken | data-identity-token | string | n/a | Signed JWT for verified identity (see below). |
| env | data-env | string | production | Environment label attached to submissions. |
| prNumber | data-pr-number | number | n/a | PR number for preview environment routing. |
| theme | data-theme | 'light' \| 'dark' \| 'system' | light | Widget color scheme. |
| triggerLabel | data-trigger-label | string | Open feedback | Tooltip text on the trigger button. |
| assistantPosition | n/a | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' | bottom-right | Corner for the floating trigger. |
| redactSelectors | n/a | string[] | [] | CSS selectors for elements to redact from DOM snapshots. |
| capturePageContext | n/a | boolean | false | Include a redacted DOM snapshot with submissions. |
| captureConsole | n/a | boolean | false | Include recent console logs with submissions. |
| captureNetwork | n/a | boolean | false | Include recent network requests with submissions. |
| sessionReplayUrlResolver | n/a | () => string \| null \| Promise<string \| null> | n/a | Returns a session replay URL (e.g. FullStory, LogRocket). |
| visualSuggestions | n/a | { enabled?: boolean } | { enabled: false } | Let reporters preview narrow visual changes before submit. |
| previewOnly | n/a | boolean | false | Show the widget in read-only mode without submitting. |
Setup
1. Create a feedback key
In your Obvious workspace, go to Autobuild > Settings and create a Feedback SDK key. You'll get:
- A public key (
fsk_pub_...) — used in the browser SDK. - A private key (
fsk_secret_...) — used server-side to sign identity tokens. Store it securely; it is only shown once.
Configure allowed domains to restrict which origins can submit feedback with this key.
2. (Optional) Verify user identity
To attach verified user information to feedback submissions, sign an identity token on your server using the private key:
import { SignJWT } from "jose";
const identityToken = await new SignJWT({
identity: { email: user.email, name: user.name },
})
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
.setSubject("fsk_pub_...")
.setIssuedAt()
.setExpirationTime("1h")
.sign(new TextEncoder().encode("fsk_secret_..."));Pass the resulting token as identityToken in the SDK config. If the token is missing or invalid, feedback is still accepted but marked as unverified.
Theming
The widget uses Shadow DOM and does not inherit host-page styles. Control appearance with:
Theme presets
ObviousFeedback.init({
publicKey: "fsk_pub_...",
theme: "dark", // 'light' (default) | 'dark' | 'system'
});light— Always light. Safe for light-only host pages.dark— Always dark.system— Follows the browserprefers-color-schememedia query. Only use when the host page also follows system preference, otherwise the widget may be invisible against the background.
CSS custom properties
Override individual tokens on :root or the widget host element:
:root {
--obv-feedback-bg: #fafafa;
--obv-feedback-primary: #0066ff;
--obv-feedback-text: #1a1a1a;
--obv-feedback-border: rgba(0, 0, 0, 0.1);
}Available tokens: --obv-feedback-bg, --obv-feedback-bg-subtle, --obv-feedback-trigger-bg, --obv-feedback-text, --obv-feedback-muted, --obv-feedback-border, --obv-feedback-border-strong, --obv-feedback-primary, --obv-feedback-primary-foreground, --obv-feedback-radius, --obv-feedback-radius-card.
API
ObviousFeedback.init(config): FeedbackSdkHandle
Initialize the widget. Only one instance can be active at a time; calling init again destroys the previous instance.
Returns a handle with:
| Method | Description |
| ------------------------------------- | ----------------------------------------------------------------------- |
| destroy() | Remove the widget from the page. |
| open() | Programmatically open the feedback card. |
| getOpenIssueCount() | Number of non-terminal issues submitted in this session. |
| subscribeToOpenIssueCount(listener) | Subscribe to open issue count changes. Returns an unsubscribe function. |
Keyboard shortcut
Press Cmd/Ctrl + Shift + . to open the feedback card when the widget is active.
Attachments
Users can attach files (up to 25 MB each, 10 per submission) via drag-and-drop or the file picker in the widget. Attachments are uploaded directly to secure storage via pre-signed URLs.
Visual Suggestions
Visual suggestions are default-off for external consumers. Enable them when you want reporters to select a page element, preview a narrow style change live, and submit that intent with the feedback payload:
ObviousFeedback.init({
publicKey: "fsk_pub_...",
visualSuggestions: { enabled: true },
});Submitted feedback includes context.visualSuggestions with { version: 1, suggestions: [...] }. The SDK only accepts font size, border radius, padding, gap, text color, and background color, and it rejects unsafe CSS syntax before applying or submitting a preview.
Local Dashboard Dogfood
Use a local package link to test SDK changes inside the Obvious dashboard before publishing:
cd /Users/alexolyaiy/Repositories/obvious-feedback-sdk
bun install
bun link
bun run devIn the dashboard package:
cd /Users/alexolyaiy/Repositories/obvious/dashboard
bun link @obvi/feedback-sdk --no-saveRestart the dashboard so Vite reloads the linked package. Before committing dashboard dependency changes, remove the local link and depend on the published prerelease package.
Status polling
The widget tracks submitted issues and shows their current status (received, in progress, resolved, etc.) in the feedback card. Status is refreshed automatically when the card is opened.
Browser support
The SDK targets ES2020 and uses Shadow DOM, ResizeObserver, and crypto.randomUUID (with fallback). It works in all modern browsers (Chrome, Firefox, Safari, Edge).
Troubleshooting
Widget is invisible
If the widget trigger blends into the page background:
- Check the
themesetting. If your page is light-only, usetheme: 'light'(the default), not'system'. - On macOS with auto light/dark mode,
theme: 'system'will switch the widget to dark when the OS is in dark mode, even if the host page stays light.
Content Security Policy (CSP)
In browsers that support constructable stylesheets, the widget applies its Shadow DOM CSS with adoptedStyleSheets, which avoids an inline <style> tag. Older browsers fall back to injecting a Shadow DOM <style> tag. If your CSP blocks inline styles in those fallback browsers, add 'unsafe-inline' to style-src for pages that load the widget.
Submissions rejected with 403
The feedback key's allowed domains must include the hostname where the SDK runs. Check your key configuration in Obvious.
License
MIT
