fdbck.

Embed the widget

The widget is one script tag. It renders a feedback button, captures page context automatically, and posts submissions to your queue.

The snippet

Paste this just before the closing </body> tag. Your widget key is on the project's Settings page.

index.html
<script
  src="https://fdbck.app/widget.js"
  data-widget-key="wk_your_key_here"
  async
></script>

data-widget-key is the only required attribute. It's a public embed key — safe to ship in client HTML. It is not your worker token.

Attributes

Every attribute the widget reads. All are optional except data-widget-key.

AttributeType / defaultDescription
data-widget-keystring · requiredPublic embed key (wk_…). Routes feedback to your project.
data-api-endpointstring · fdbck.app/api/v1/feedbackOverride the full POST endpoint. For local testing against a dev server.
data-source-rootstring · max 200App context label sent with every submission (e.g. marketing-site).
data-componentstring · max 300Current component or page name. Helps triage locate the right code.
data-app-versionstring · max 80Semver of the running build. Useful for reproducing regressions.
data-contextJSON stringArbitrary context object, sent verbatim. See the JS hook below for dynamic values.
data-hide-on-mobilebool · falseHide the button below the mobile breakpoint.
data-hide-on-scrollbool · falseTuck the button away while the page is scrolling.
data-mobile-breakpointnumber · 768Viewport width (px) below which mobile rules apply.
data-hide-fabbool · falseDon't render the floating button — drive the modal from your own triggers.
data-accenthex colorAccent color for the button and panel (e.g. #d97706).
data-z-indexnumber · 40Stacking order, in case the button collides with your own UI.
data-themeauto | light | dark · autoColor scheme. auto follows the system preference.

Dynamic context

For values that change at runtime — the signed-in user's plan, a feature flag, the current record id — define window.fdbck.context. It's an optional function that returns an object, merged into the submission at the moment the user sends it.

javascript
window.fdbck = window.fdbck || {};
window.fdbck.context = () => ({
  plan: currentUser.plan,
  orgId: currentUser.orgId,
  route: location.pathname,
});

The hook's returned keys merge over the static data-source-root, data-component, and data-app-version attributes, so the hook wins on those. data-context is carried through as its own nested object — it isn't key-merged with the hook, so the two never collide.

Open it from your own UI

The floating button is just the default launcher. You can open the same modal from any element you already have — a branded button, a footer Support link, a menu item. Two ways, use either or both.

1 · Add a data attribute

Put data-fdbck on any element and the widget wires up the click for you — no JavaScript. It works for elements rendered after load too (SPA routes, late-mounted footers).

html
<!-- Opens the modal -->
<button data-fdbck>Send feedback</button>

<!-- A footer link (navigation is suppressed) -->
<a href="#" data-fdbck>Support</a>

<!-- Preselect a type, or toggle open/closed -->
<button data-fdbck data-fdbck-type="bug">Report a bug</button>
<button data-fdbck="toggle">Feedback</button>
AttributeValuesDescription
data-fdbck"open" · defaultOpens the modal on click. Also accepts "close" or "toggle".
data-fdbck-typebug | feature | otherPreselect the feedback type when this trigger opens the modal.

2 · Call the JS API

For framework components or programmatic control, the widget publishes window.fdbck once it loads.

javascript
fdbck.open();                       // open the modal
fdbck.open({ type: "feature" });    // open with a type preselected
fdbck.close();
fdbck.toggle();

Going launcher-less? Add data-hide-fab="true" to the script tag to drop the floating button and drive the modal entirely from your own triggers.

Captured automatically

Every submission carries page context the widget gathers on its own — the current route, the page title and headings, visible UI text, the detected front-end framework, and any test/component hints in the markup (data-testid, data-component, data-cy, data-test) — so triage has something to work with even when the user's description is terse.

PII is scrubbed before it leaves the page. Email addresses, phone numbers, and long digit runs are redacted from captured text. Anything you pass yourself through data-context or the JS hook is sent as-is — keep secrets out of it.

Repro capture (screenshot & diagnostics)

Two opt-in capture layers turn a vague report into a reproducible one — both off by default, enabled per project in Settings → Repro capture:

  • Screenshot. Reporters get an explicit “Include a screenshot of this page” checkbox — nothing is captured until they tick it, and they see (and can retake or remove) the exact image before sending. Rendered in-browser (no screen-share prompt), downscaled and compressed (~60–250KB). Mark any element data-fdbck-mask to redact it (a solid gray box) in every screenshot; password fields are always masked. Screenshots are visible only to you in the dashboard — never on the board or status page.
  • Diagnostics. The widget records console errors/warnings and failed network requests (method, path, status, duration — never headers, cookies, bodies, or query strings), disclosed to the reporter at submit time with a “view” expander showing exactly what will be sent. PII-scrubbed twice (client and server) and attached to the item for you and your agent.

Both layers load as separate lazy chunks, so the core widget stays under its 8KB budget and pages where capture is off pay zero extra bytes. Known screenshot limits: webfonts render as system fallbacks, cross-origin images become placeholders, and very large pages (>8,000 elements) skip capture rather than jank.

Post-submit experience

What the user sees after they hit send is owner-configured in Settings → Widget messaging — the confirmation title and body, up to 3 footer links, a “Powered by fdbck” toggle, and an optional redirect URL. No snippet changes: the widget fetches this config on load and patches it in, so edits show up without re-embedding.

The submitter also gets a Track your feedback status link in the confirmation automatically, and the textarea shows a live character counter with a gentle minimum-length nudge so near-empty reports get a little more detail. The message field caps at 1000 characters (the API accepts up to 2000 if you post directly — see the Feedback API).

The form also shows an optional email field — “Email — optional, we'll tell you when it's fixed.” If the reporter fills it in, they get a single email when the fix for their item is merged; leaving it blank changes nothing, and a malformed address is silently ignored — it never blocks the submission. See Notifications for how the loop closes and the privacy posture.

React to a submit

Assign window.fdbck.onSubmitSuccess to run your own code after a successful submit — deep-link the user to their status page, fire an analytics event, show a toast. It's called with { id, statusUrl }. Errors in your handler are caught, so a throw can't break the widget.

javascript
window.fdbck = window.fdbck || {};
window.fdbck.onSubmitSuccess = ({ id, statusUrl }) => {
  // e.g. take the user straight to their status page
  if (statusUrl) window.open(statusUrl, "_blank");
  analytics.track("feedback_submitted", { id });
};