FeedbackHiFeedbackHi

Docs

One script tag and one element. No npm install, no build step, no API key in your markup — everything below is optional refinement on top of those two lines.

Quick start

Create a project in your dashboard, copy its ID, and paste this anywhere in your HTML — the end of <body> is a good place.

index.html
<script src="https://feedbackhi.com/sdk.js" async></script>
<feedback-widget project-id="a1b2c3d4"></feedback-widget>

That gives you the floating widget: a small button in the corner that opens a one-box composer. Reload your site and it is live — the first post shows up in your dashboard immediately.

Where the two lines go

Anywhere inside <body> works — the script has no build step and the element positions itself. What you're really looking for is the one file every page on your site already shares, so you paste once and the button shows up everywhere.

An AI editor (Cursor, Claude Code, Lovable, v0, Bolt)
Don't hunt for the file. Open your project in the dashboard, hit Copy prompt for AI agent, and paste it into the chat — it carries your project ID and tells the agent exactly what to add and where.
A plain HTML site
Just before the closing </body> tag in index.html. If your pages are separate HTML files with no shared template, paste it into each one you want the button on.
Next.js
app/layout.tsx — the script via next/script and the element inside <body>, so every route gets it. See React, Next.js, Vue below for the snippet.
Vite, Create React App, Vue, Svelte, Astro
The index.html at the project root — before </body>. It sits outside your component tree, so no framework wiring and no re-render can disturb it.
WordPress
Appearance → Theme File Editor → footer.php, before </body>. On a theme you don't want to edit, any “insert headers and footers” plugin does the same job — paste both lines into the footer box.
Webflow
Project settings → Custom code → Footer code, then publish. Paste both lines together.
Framer
Project settings → General → Custom code → End of <body> tag, then publish.
Squarespace
Settings → Advanced → Code injection → Footer. Business plan or above, as with any third-party code there.
Shopify
Online Store → Themes → … → Edit code → layout/theme.liquid, before </body>.
Wix
Settings → Custom code → Add code, set to Body — end on all pages.
No site at all
Skip the embed. Every project has a public board at /b/your-project that works on its own — share the link from your dashboard.

Pasted it and see nothing? The floating button draws itself the moment the script runs, so a missing button means the script tag never loaded — check the URL, and republish if your host needs it. A button that opens onto an error instead of a composer means the project-id doesn't match any project in your dashboard.

Widget, tab, or board

Three elements ship in the same script. Pick one way in per page — two of them on the same screen is two things to ignore, not two chances to be heard.

<feedback-widget>

A floating button and slide-in panel, in a bottom corner. Best on an app or landing page where feedback is a side door, not the point of the page.

<feedback-tab>

A small tab on the left or right edge, halfway down, that slides the same panel out beside it. Best when your corners are already taken — by a chat bubble, a cookie bar, a cart button.

<feedback-board>

The full board, inline and full width: posts, votes, statuses, official replies. Best on a dedicated /feedback or /roadmap page.

roadmap.html
<feedback-board project-id="a1b2c3d4"></feedback-board>

The tab is fixed to the viewport, so it goes anywhere inside <body> — once, in whatever file every page shares. On its own it is the icon alone; give it a label to add the word sideways along the edge.

layout.html
<feedback-tab project-id="a1b2c3d4" side="right"></feedback-tab>

Attributes

Shared by all three elements:

AttributeValueWhat it does
project-idrequiredThe project's ID or slug — copy it from your dashboard.
sort"top" | "new"Which order posts load in. Defaults to "top"; visitors can switch.
data-apioriginPoint the element at a different API origin. Only useful in local development.

Widget only — the dashboard's embed panel writes these for you:

AttributeValueWhat it does
label"Feedback"Text on the floating button.
position"bottom-right" | "bottom-left"Which corner the button sits in. Use the free one if you already have a chat bubble.
ask-questiontextThe single question an auto-prompt asks. Defaults to "What's one thing we could do better?".
auto-aftersecondsAsk on its own after this many seconds of active use. A tab left open overnight never counts. Omit it and the widget only opens when clicked.
auto-visitsnumberHold the auto-prompt back until someone's Nth visit. Defaults to 1.

Side tab only:

AttributeValueWhat it does
side"right" | "left"Which edge the tab rides. Defaults to the right.
labeltextOmit it and the tab is the icon alone, which is the least it can take from your own page. Set it and the word is drawn sideways along the edge, under the icon.

Theming

The accent colour comes from the project itself, so changing it in the dashboard changes every site you embedded it on. To override that per page — or to match a specific design — set the CSS variables on the element. They pierce the shadow boundary; nothing else does.

styles.css
feedback-board,
feedback-widget,
feedback-tab {
  --fb-primary: #7c3aed;   /* accent — overrides the dashboard colour */
  --fb-radius: 4px;        /* corner radius */
  --fb-bg: #ffffff;        /* card background */
  --fb-surface: #f5f5f5;   /* inset background */
  --fb-text: #171717;
  --fb-muted: #737373;
  --fb-border: #e5e5e5;
  --fb-font: inherit;
  --fb-max-width: 1100px;  /* board only */
}

Asking at the right moment

The highest-quality feedback arrives seconds after someone succeeds at something. The widget can ask on its own after a while (auto-after), but calling it from your own success path works better, because only your code knows when the export finished.

app.js
<feedback-widget project-id="a1b2c3d4" ask-question="How was the export?">
</feedback-widget>

<script>
  // Called from your own success path — after an export finished,
  // a signup completed, a checkout went through.
  window.feedbackhi?.ask?.("after-export");
</script>

The label is for your own analytics — it is passed to the fb:prompt event and is not sent to FeedbackHi. The widget still respects its own limits: it will not interrupt someone mid-sentence, on a hidden tab, or after they have already answered or waved it away twice.

If your code may run before the async script has loaded, add this stub above it — calls are queued and replayed once the SDK arrives:

index.html
<script>
  window.feedbackhi = window.feedbackhi || { q: [], ask(label) { this.q.push(label); } };
</script>

Events

Every element dispatches bubbling custom events, so you can hook your own analytics in.

app.js
document.addEventListener("fb:submit", (event) => {
  console.log("new feedback", event.detail.feedback);
});

document.addEventListener("fb:prompt", (event) => {
  console.log("asked", event.detail.label);
});

document.addEventListener("fb:prompt-dismiss", () => {
  console.log("waved away");
});

React, Next.js, Vue

They are standard custom elements, so any framework that renders HTML can render them. In Next.js, load the script with next/script and put the element in your layout or a single page:

app/page.tsx
import Script from "next/script";

return function Page() {
  return (
    <>
      <Script src="https://feedbackhi.com/sdk.js" strategy="afterInteractive" />
      <feedback-widget project-id="a1b2c3d4" />
    </>
  );
}

In TypeScript you will want to declare the tags once, in a .d.ts file, so JSX knows about them. Vue users should mark both tags as custom elements in the compiler options; plain HTML needs nothing at all.

What it stores on your visitors

No cookies and no cross-site tracking. The SDK keeps a random identifier plus a couple of prompt counters in the visitor's own local storage, so votes count once and nobody is asked the same question twice. Votes and rate limits use a salted one-way hash, not a stored IP address. The full detail is in the Privacy Policy.

FAQ

Do visitors need an account?

No. They type a sentence and send it — no sign-up, no email, no captcha. A name and email are optional fields they can leave blank.

Is the board public?

Yes — posts, statuses, and vote counts are visible to anyone who can see the page you embedded it on. Email addresses are the exception: only you see those, in the dashboard. Any post can be hidden or deleted from there.

How are duplicate votes handled?

Each browser gets a random identifier, and one vote per post is counted against it. It also feeds the rate limits: five posts and ten screenshot uploads per ten minutes, sixty votes a minute.

Can visitors reply to each other?

Not today. FeedbackHi is a feedback board, not a comment thread: visitors post and vote, and you can add one official reply per post, marked as yours.

Can I use one project for several sites?

You can — the same two lines work anywhere — but everything lands in one board. Separate sites usually want separate projects, which cost nothing and all sit in the same dashboard.

Will it clash with my CSS?

No. Every element renders inside a shadow root, so your page's styles cannot leak in and theirs cannot leak out. The CSS variables above are the deliberate way through.

Still stuck, or missing something?

Post it on our own board — we read every one, and you can watch the status change.