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.
<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 inindex.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 vianext/scriptand 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.htmlat 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-projectthat 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.
<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.
<feedback-tab project-id="a1b2c3d4" side="right"></feedback-tab>
Attributes
Shared by all three elements:
| Attribute | Value | What it does |
|---|---|---|
| project-id | required | The 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-api | origin | Point the element at a different API origin. Only useful in local development. |
Widget only — the dashboard's embed panel writes these for you:
| Attribute | Value | What 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-question | text | The single question an auto-prompt asks. Defaults to "What's one thing we could do better?". |
| auto-after | seconds | Ask 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-visits | number | Hold the auto-prompt back until someone's Nth visit. Defaults to 1. |
Side tab only:
| Attribute | Value | What it does |
|---|---|---|
| side | "right" | "left" | Which edge the tab rides. Defaults to the right. |
| label | text | Omit 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.
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.
<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:
<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.
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:
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.