Skip to content

Install guide · React

Add the Quackback widget to React

The @quackback/widget package ships React hooks, so the widget boots when your app mounts and tears down when it unmounts. There's no provider to wrap your tree in. This guide adds it to a Vite or Create React App project.

Get your widget snippet

  • 14-day Pro trial, no card needed
  • Your workspace URL and snippet, ready to copy
  • The widget is on every plan, including Free

The widget your customers see inside your app.

Your team’s viewYour customers’ view
Feedback
Review
Status
Board
Tags

Install the widget in React

First, in Quackback, open Settings, then Widget, and turn on Show on your website. Until it's on, the snippet loads but the launcher stays hidden. Every snippet uses your-workspace.quackback.io as a placeholder. Swap in your workspace URL, or your custom domain, from Settings, under Widget, then Install.

Read the full install docs

Install the package

Add @quackback/widget with your package manager. The React hooks live in the same package, under @quackback/widget/react, and need React 19.

Terminal
npm install @quackback/widget

Init the widget in your app shell

Call useQuackbackInit once, in the component that wraps every page. Put your workspace URL in a public env variable. The launcher appears for every visitor, signed in or not.

src/App.tsx
import { useQuackbackInit } from "@quackback/widget/react";

export function App() {
  useQuackbackInit({
    instanceUrl: import.meta.env.VITE_QUACKBACK_URL,
  });

  return <Layout />;
}

// .env
// VITE_QUACKBACK_URL=https://your-workspace.quackback.io

Open it from your own button

useQuackback returns the widget, so any component can open it straight to a view. Pass launcher: false to useQuackbackInit if you'd rather hide the floating button.

src/FeedbackButton.tsx
import { useQuackback } from "@quackback/widget/react";

export function FeedbackButton() {
  const qb = useQuackback();
  return (
    <button onClick={() => qb.open({ view: "new-post" })}>
      Send feedback
    </button>
  );
}

Identify signed-in users

Your server signs a five-minute token for the signed-in user, and the hook passes it to identify. Posts, votes and chats then belong to the right customer, and nobody can pose as someone else.

How identify works

Sign a token on your server

Copy the signing secret from Settings, under Widget, then Install, into a server-only env variable. Add a route that returns { ssoToken } for the current session, and 401 when nobody is signed in. This example uses Express and jose.

server/widget-sso.ts
import { SignJWT } from "jose";

const secret = new TextEncoder().encode(process.env.WIDGET_SIGNING_SECRET);

app.get("/api/widget-sso", async (req, res) => {
  const user = req.user; // from your auth middleware
  if (!user) return res.status(401).end();

  const ssoToken = await new SignJWT({
    sub: String(user.id), // stable user id, never the email
    email: user.email,
    name: user.name,
  })
    .setProtectedHeader({ alg: "HS256" })
    .setExpirationTime("5m")
    .sign(secret);

  res.json({ ssoToken });
});

Pass it to useQuackbackInit

Fetch the token once when you know who's signed in and hand it to identity. The hook calls identify when the token arrives. Call Quackback.logout() in your sign-out handler, because clearing identity alone doesn't sign the widget out.

src/App.tsx
import { useEffect, useState } from "react";
import { Quackback, useQuackbackInit } from "@quackback/widget/react";

export function App() {
  const { user } = useAuth(); // your auth hook
  const [ssoToken, setSsoToken] = useState<string>();

  useEffect(() => {
    if (!user) return;
    fetch("/api/widget-sso")
      .then((res) => (res.ok ? res.json() : null))
      .then((data) => data && setSsoToken(data.ssoToken));
  }, [user]);

  useQuackbackInit({
    instanceUrl: import.meta.env.VITE_QUACKBACK_URL,
    identity: ssoToken ? { ssoToken } : undefined,
  });

  return <Layout />;
}

// In your sign-out handler
Quackback.logout();

Good to know on React

Widget events and methods
Init options are read onceuseQuackbackInit captures options like placement and locale at mount. Only identity changes are picked up later. To change the rest, call Quackback.destroy() and remount.
Strict Mode runs it twiceIn development, React mounts, unmounts and remounts effects. The widget inits, tears down and inits again. That's expected, and production runs it once.
Logout is explicitSetting identity back to undefined doesn't sign anyone out. Call Quackback.logout() on sign-out so the next person on a shared browser doesn't inherit the last identity.
Keep the secret on the serverAnything in a VITE_ variable ships to the browser. Only the workspace URL goes there. The signing secret stays in your backend's environment.

Common questions

Do I need a provider component?

No. The widget is a singleton, and the hooks wrap its lifecycle. useQuackbackInit boots it, useQuackback returns it, and useQuackbackEvent subscribes to events such as vote or post:created for the life of a component.

Which React versions are supported?

The hooks in @quackback/widget/react list React 19 as a peer dependency. On older React, import Quackback from @quackback/widget and call Quackback.init in a useEffect instead.

Do visitors have to sign in to use the widget?

No. Once Show on your website is on in your widget settings, the launcher appears for every visitor after init. Anonymous visitors can still post, vote and chat, and can leave an email inline. When you identify them later, what they did before signing in moves onto their account.

Is the Quackback widget free?

Yes. The widget is on every plan, including Free, with feedback, live chat, help articles and your changelog inside it. Every signup starts with a 14-day Pro trial.

Have another question? Talk to the team.

Put feedback inside your React app