Install guides / React Router and Remix
Bug reports from your React Router app
In React Router's framework mode, and in Remix, app/root.tsx renders the document for every route, so the widget goes there once. Use the tag for the quickest start, or the React components to say who is signed in.
The tag, in your root Layout
Put it after <Scripts />. It runs once from the server-rendered HTML; hydration leaves it and the widget's element alone.
import { Links, Meta, Scripts, ScrollRestoration } from "react-router"
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
<script
src="https://app.brainbox.sh/widget.js"
data-project="pk_your_project_key"
data-endpoint="https://app.brainbox.sh/ingest"
/>
</body>
</html>
)
}On Remix, the imports come from @remix-run/react instead; the file is the same.
With the signed-in user
Load the user in the root loader and hand it to the React components. They start in an effect, so server rendering is unaffected.
npm install @ganeshdole/brainbox-widget @ganeshdole/brainbox-react @ganeshdole/brainbox-coreimport type { ReactNode } from "react"
import { brainboxTransport } from "@ganeshdole/brainbox-core"
import { BrainboxProvider } from "@ganeshdole/brainbox-react"
import { BrainboxWidget } from "@ganeshdole/brainbox-widget"
const transport = brainboxTransport({
endpoint: "https://app.brainbox.sh/ingest",
projectKey: "pk_your_project_key",
})
type User = { id: string; email: string; name: string }
export function Feedback({ user, children }: { user: User | null; children: ReactNode }) {
return (
<BrainboxProvider transport={transport} identity={user ?? undefined}>
{children}
<BrainboxWidget />
</BrainboxProvider>
)
}import { Outlet } from "react-router"
import type { Route } from "./+types/root"
import { Feedback } from "./feedback"
import { getUser } from "./auth.server" // yours
export async function loader({ request }: Route.LoaderArgs) {
return { user: await getUser(request) }
}
export default function App({ loaderData }: Route.ComponentProps) {
return (
<Feedback user={loaderData.user}>
<Outlet />
</Feedback>
)
}On Remix, read the loader's data with useLoaderData() instead of the loaderData prop.
Your own button
Under <Feedback>, any route can open the widget with useBrainboxWidget():
import { useBrainboxWidget } from "@ganeshdole/brainbox-widget"
export function ReportProblemButton() {
const { start } = useBrainboxWidget()
return <button onClick={() => start("screenshot")}>Report a problem</button>
}Passwords and personal data
Typed values are always masked in recordings, so passwords, emails and card numbers never leave the browser as plain text. Add the rr-block or rr-mask class to anything else you want kept out.
Try it before you install it
The button in the corner of this page is the real widget. Report something on this page and it lands in our inbox, the way your users’ reports will land in yours.
Get started