npm Package & Programmatic API
The script-tag embed covers most websites. If you are building a single-page app and want lifecycle control (mount on consent, unmount on route change, open the panel from your own button), use the programmatic API instead. It comes in two flavours that share the same options and behavior.
Option A: script tag + global API (no build step)
Load the CDN script without data-app-key. The widget will not auto-mount; instead it exposes a small global:
<script src="https://cdn.anonfeedback.io/widget/v1/widget.js" async></script>
const widget = AnonfeedbackWidget.init({
appId: "YOUR_APP_KEY",
position: "bottom-right",
});
widget.open(); // open the feedback panel programmatically
widget.close(); // close it
widget.destroy(); // remove the widget and all its DOM
AnonfeedbackWidget.version; // e.g. "1.1.3"
Option B: npm package (bundled into your app)
npm install @anonfeedback/widget
import { mount } from "@anonfeedback/widget";
const widget = mount({ appId: "YOUR_APP_KEY" });
The package ships ESM with TypeScript types and has no peer dependencies (its rendering runtime is bundled). The npm version always matches the CDN artifact version, so @anonfeedback/[email protected] is byte-equivalent in behavior to /widget/1.1.3/widget.js. Both are bumped together on every release; install @anonfeedback/widget without a version to always get the latest.
React example
import { useEffect } from "react";
import { mount } from "@anonfeedback/widget";
function FeedbackWidget() {
useEffect(() => {
const widget = mount({ appId: "YOUR_APP_KEY" });
return () => widget.destroy();
}, []);
return null;
}
Options
Both AnonfeedbackWidget.init() and mount() accept the same options:
| Option | Type | Description |
|---|---|---|
appId | string | Required. Public App ID from Create → Apps. |
position | "bottom-right" | "bottom-left" | "bottom-center" | Overrides the position set in the app. |
triggerLabel | string | Overrides the trigger button label. |
theme | "dark" | "mini" | Overrides the theme set in the app. |
The returned handle:
| Method | Effect |
|---|---|
open() | Opens the feedback panel (same as clicking the trigger). |
close() | Closes the panel. |
destroy() | Unmounts the widget and removes its DOM entirely. Safe to call on route change. |
Origin validation still applies
Regardless of how the widget is loaded, every config fetch and submission is validated server-side against the app's allowed origins. Add your SPA's origin (and your staging origin) in Create → Apps before testing.
Theming works the same everywhere
All three consumption modes (auto-init script tag, global API, npm) render the identical widget, so the design tokens behave the same: dashboard-configured custom themes arrive via the config API, and your own CSS can override tokens on the #anonfeedback-widget mount element.