Skip to content

Install guide · Remix

Add the Quackback widget to Remix

Remix renders on the server and hydrates in the browser. The root loader hands the workspace URL to the client, the React hooks boot the widget after hydration, and a resource route signs identify tokens.

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 Remix

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. The React hooks need React 19.

Terminal
npm install @quackback/widget

Pass the workspace URL from the root loader

Remix doesn't expose server env variables to the browser. Return the URL from the root loader instead.

app/root.tsx
import { json } from "@remix-run/node";

export async function loader() {
  return json({ quackbackUrl: process.env.QUACKBACK_URL! });
}

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

Init the widget in the root component

Effects only run in the browser, so the hook is safe in a server-rendered root.

app/root.tsx
import { Outlet, useLoaderData } from "@remix-run/react";
import { useQuackbackInit } from "@quackback/widget/react";

export default function App() {
  const { quackbackUrl } = useLoaderData<typeof loader>();
  useQuackbackInit({ instanceUrl: quackbackUrl });

  return <Outlet />;
}

Identify signed-in users

A resource route signs a five-minute token for the signed-in user. The root component fetches it once and passes it to identify.

How identify works

Add a resource route that signs the token

Copy the signing secret from Settings, under Widget, then Install, into WIDGET_SIGNING_SECRET. Read the user with your session helper.

app/routes/api.widget-sso.ts
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { SignJWT } from "jose";
import { getUser } from "~/session.server"; // your session helper

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

export async function loader({ request }: LoaderFunctionArgs) {
  const user = await getUser(request);
  if (!user) throw new Response("Unauthorized", { status: 401 });

  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);

  return json({ ssoToken });
}

Identify from the root component

Fetch once after hydration and hand the token to the hook. Call Quackback.logout() before your logout action redirects.

app/root.tsx
import { useEffect, useState } from "react";

export default function App() {
  const { quackbackUrl } = useLoaderData<typeof loader>();
  const [ssoToken, setSsoToken] = useState<string>();

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

  useQuackbackInit({
    instanceUrl: quackbackUrl,
    identity: ssoToken ? { ssoToken } : undefined,
  });

  return <Outlet />;
}

Good to know on Remix

Widget events and methods
Don't sign in the root loaderThe root loader reruns on navigations and form submissions. A fresh token each time would identify again and again. A separate resource route, fetched once, avoids that.
React Router 7Remix's successor uses the same pattern. Import from react-router, return plain objects from loaders, and register the resource route in app/routes.ts.
Logout is explicitClearing the token doesn't sign the widget out. Call Quackback.logout() on sign-out.

Common questions

Why fetch the token instead of rendering it?

Tokens expire after about five minutes, and the root loader reruns often. Fetching once per page load identifies the user exactly once, and the widget keeps its own session after that.

Can I use the script tag in Remix?

Yes. Put the snippet from Settings in your root document's head. The npm package is easier to type-check and lets you use the hooks.

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 Remix app