Install the widget
Add the widget to your site with your coding agent, a script tag, or npm (React hooks included), then control it with init options and SDK methods like open, identify, and logout.
This article shows how to add the widget to your site or app, either by handing a prompt to your coding agent or by installing it yourself with a script tag or npm, and lists the SDK options and methods.
Install with your coding agent (recommended)
- Go to Settings > Widget > Install.
- Under 1. Copy the prompt for your agent, click Copy install prompt.
- Paste the prompt into the coding agent working in your app's repository (the host app).
- After the agent finishes, open a page on your site (localhost is fine). The install page shows when the widget connects.
The prompt includes a short-lived pairing code. The agent redeems it to fetch your workspace's signing secret and writes the secret to a server-only environment variable in your app (any name works). The secret never appears in the chat. Redeeming the code also turns on Show on your website.
If your app already has sign-in, the agent also identifies signed-in users. On a static or logged-out site, it stops after init, and the widget runs for anonymous visitors.
After the widget connects, the Install page becomes a manage screen with the connection status, visibility, Add to another site, and the signing secret.
Install by hand
Script tag
- Go to Settings > Widget > Install and open Install without an agent.
- Click Copy snippet and paste it before the closing
</body>tag on your site. - Make sure Show on your website is on in Settings > Widget. If it is off, the launcher stays hidden even after the snippet loads.
The snippet looks like this (your workspace URL is filled in for you):
<script>
(function(w,d){if(w.Quackback)return;w.Quackback=function(){
(w.Quackback.q=w.Quackback.q||[]).push(arguments)};
var s=d.createElement("script");s.async=true;
s.src="https://feedback.example.com/api/widget/sdk.js";
d.head.appendChild(s)})(window,document);
Quackback("init");
// Quackback("identify", { ssoToken });
// Quackback("logout");
</script>The snippet creates a command queue and then loads the SDK. Calls you make before the script loads run automatically once it does. Script-tag installs do not need instanceUrl, because the SDK reads it from sdk.js.
npm
npm install @quackback/widgetimport { Quackback } from "@quackback/widget";
Quackback.init({ instanceUrl: "https://feedback.example.com" });instanceUrl is required with the npm package. Vue, Svelte, Angular, and Solid apps call Quackback the same way.
React
import { useQuackbackInit, useQuackback, useQuackbackEvent } from "@quackback/widget/react";
function App() {
useQuackbackInit({ instanceUrl: "https://feedback.example.com" });
useQuackbackEvent("post:created", (post) => {
analytics.track("feedback_submitted", { postId: post.id });
});
return <Layout />;
}
function FeedbackButton() {
const qb = useQuackback();
return <button onClick={() => qb.open({ view: "new-post" })}>Feedback</button>;
}No provider is needed. The hooks wrap the single SDK instance.
Tip: Identifying users is optional, and the launcher works without it. For signed-in attribution, fetch a token signed by your server, never a raw id or email. See Identify users.
Init options
Quackback("init", {
placement: "left",
defaultBoard: "feature-requests",
locale: "fr",
});Option | Type | Default | Description |
|---|---|---|---|
| string | - | Your Quackback URL. Required for npm. Built into the script-tag |
|
|
| Corner for the launcher and panel until the admin settings load |
| string | - | Board slug to filter the Feedback tab. Omit to show all boards. |
| boolean |
| Set |
| string | Auto-detected |
|
| object | - | Identify at init with |
Note: Settings > Widget > Layout controls the launcher position, label, and greeting. The admin position overrides
placementonce the widget loads its settings.
Use your own button
When launcher is false, open the panel from your own UI:
Quackback.init({
instanceUrl: "https://feedback.example.com",
launcher: false,
});
document.getElementById("feedback-btn").addEventListener("click", () => {
Quackback.open();
});Script-tag calls use the same names: Quackback("open"), Quackback("identify", { ssoToken }).
SDK methods
Method | Description |
|---|---|
| Create the launcher and panel. |
| Attribute activity to a signed-in user. Do not call it for anonymous visitors. |
| Clear the identity. The widget stays visible for anonymous use. |
| Open the panel, optionally to a specific view (see below) |
| Close the panel |
| Show or hide the floating button |
| Attach session context to submitted feedback. Pass |
| Subscribe to an event. Returns an unsubscribe function. |
| Remove one handler, or every handler for that event |
| Remove the widget and its state from the page |
| Whether the panel is open |
| The identified user, or |
|
|
open
Quackback.open();
Quackback.open({ view: "new-post", title: "Bug: page crashes on save", board: "bugs" });
Quackback.open({ view: "changelog" });
Quackback.open({ view: "help", query: "pricing" });
Quackback.open({ view: "chat" });
Quackback.open({ view: "tickets" });
Quackback.open({ postId: "post_01h..." });
Quackback.open({ articleId: "article_01h..." });Option | Description |
|---|---|
|
|
| Prefill the post title with |
| Prefill the post body with |
| Board slug to preselect with |
| Prefill the help search with |
| Open one changelog entry with |
| Open a specific post |
| Open a specific help article (id or slug) |
postId and articleId take priority over view. If the target's tab is turned off, or the board is not visible to the visitor, the panel opens without switching to it.
logout, close, destroy
Quackback.logout();
Quackback.close();
Quackback.destroy();Call logout from your app's logout handler. Call destroy in single-page apps when you unmount the page that hosts the widget.
Single-page apps
Initialize once in a layout and clean up on unmount. The second effect is only needed if your app has sign-in and your server signs widget tokens.
import { useEffect } from "react";
import { Quackback } from "@quackback/widget";
import { useAuth } from "@/hooks/use-auth";
export function FeedbackWidget() {
const { user } = useAuth();
useEffect(() => {
Quackback.init({ instanceUrl: "https://feedback.example.com" });
return () => Quackback.destroy();
}, []);
useEffect(() => {
if (!user) return;
fetch("/widget-token")
.then((res) => res.json())
.then(({ ssoToken }) => Quackback.identify({ ssoToken }));
}, [user]);
return null;
}The SDK queues identify until the panel is ready, so you do not need to wait for a ready event.
Related articles
Was this helpful?
Your feedback shapes what we write next.
