Install guides / React
Bug reports from your React app
Brainbox ships as React components as well as a script tag. In a React app the components are the better fit: the widget knows who is signed in, opens from your own buttons through a hook, and lets you swap any step of the flow for a component of yours.
This page is for a client-rendered React app, such as one made with Vite. For server rendering see Next.js or React Router and Remix.
Install
npm install @ganeshdole/brainbox-widget @ganeshdole/brainbox-react @ganeshdole/brainbox-coreReact 19 is a peer dependency: the widget uses your React rather than bundling its own. The first load is a few KB; the screens and their stylesheet are a separate chunk, fetched when the launcher first shows.
Wrap your app
<BrainboxProvider> owns one Brainbox. Make its transport once, outside any component: a new transport means a new instance. identity is who is reporting; when it changes, the next report says so.
import type { ReactNode } from "react"
import { brainboxTransport } from "@ganeshdole/brainbox-core"
import { BrainboxProvider } from "@ganeshdole/brainbox-react"
import { BrainboxWidget } from "@ganeshdole/brainbox-widget"
import { useCurrentUser } from "./auth" // yours
const transport = brainboxTransport({
endpoint: "https://app.brainbox.sh/ingest",
projectKey: "pk_your_project_key",
})
export function Feedback({ children }: { children: ReactNode }) {
const user = useCurrentUser()
return (
<BrainboxProvider
transport={transport}
identity={user ? { id: user.id, email: user.email, name: user.name } : undefined}
>
{children}
<BrainboxWidget />
</BrainboxProvider>
)
}import { createRoot } from "react-dom/client"
import { App } from "./app"
import { Feedback } from "./feedback"
createRoot(document.getElementById("root")!).render(
<Feedback>
<App />
</Feedback>,
)<BrainboxWidget /> renders into a shadow root on <body> wherever it sits in your tree, so your CSS can't restyle it and no parent's overflow or transform can clip its panels.
Your own button
useBrainboxWidget() works anywhere under the provider. open() shows the chooser; start() goes straight into a screenshot, a recording or a feature request. Pass trigger="manual" to the widget to hide our floating button.
import { useBrainboxWidget } from "@ganeshdole/brainbox-widget"
export function HelpMenu() {
const { open, start } = useBrainboxWidget()
return (
<menu>
<button onClick={open}>Report a problem</button>
<button onClick={() => start("feature")}>Request a feature</button>
</menu>
)
}Details from your app
fields puts your own details beside every report: the plan, the account, the feature flag that was on. highlight: true shows a value under the reporter's name; type: "id" sets it in monospace, and email, phone and url become links.
<BrainboxProvider
transport={transport}
identity={identity}
fields={[
{ label: "Plan", value: account.plan, highlight: true },
{ label: "Account ID", value: account.id, type: "id" },
]}
>Replace one step with your own
components swaps any step of the flow for a component of yours and keeps the rest of ours. Here the composer is yours; marking up, recording and sending stay Brainbox's. Define the object outside render, or the step remounts.
import { useState } from "react"
import { BrainboxWidget, type ComposerProps } from "@ganeshdole/brainbox-widget"
function MyComposer({ screenshotUrl, onSubmit, onCancel }: ComposerProps) {
const [text, setText] = useState("")
return (
<div>
{screenshotUrl && <img src={screenshotUrl} alt="" />}
<textarea value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => onSubmit(text, null)}>Send</button>
<button onClick={onCancel}>Cancel</button>
</div>
)
}
const components = { Composer: MyComposer }
<BrainboxWidget components={components} />Your component renders inside the widget's shadow root, so bring its CSS with it: a <style> inside it, or inline styles.
Or the script tag
No npm install: paste the tag before </body> in index.html. It brings its own copy of React.
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script
src="https://app.brainbox.sh/widget.js"
data-project="pk_your_project_key"
data-endpoint="https://app.brainbox.sh/ingest"
></script>
</body>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