Install Flawsnap in a React app
Load the widget once in your HTML shell, not in a component, then tell it who is signed in from your auth code.
Steps
- 1
Allow your dev and production hosts
In the Flawsnap project’s Security settings, add your production domain, any staging host and your dev server, such as
localhost:5173for Vite. - 2
Add the snippet to index.html
Paste the Flawsnap snippet into the
<head>ofindex.html(the project root in Vite,public/index.htmlin older setups). - 3
Add the global type
If you use TypeScript, add a small declaration so
window.Flawsnaptype-checks. - 4
Identify signed-in users
Where your app learns who is signed in, call
window.Flawsnap?.identify({ name, email }). - 5
Run the app and send a report
Start the dev server, click the button and send a test report. It appears in the project’s reports.
Load it once, in index.html
The simplest and most complete install is the HTML shell your React app mounts into. The widget then loads before React does, sees console errors from the first render, and stays for the life of the tab.
<head>
<meta charset="UTF-8" />
<title>My app</title>
<script src="https://backend.flawsnap.com/widget.js" data-key="pk_…" async></script>
</head>Why not a component
Do not render the script tag from a page component or load it in a per-route effect. It only needs to run once per tab. Flawsnap follows client-side navigation itself: it watches history.pushState, replaceState and the back button, so React Router route changes are picked up, and recordings carry on across them.
If you must load it from code, do it once at the app root and guard it. Flawsnap ignores a second copy anyway, but the guard stops this file adding the script twice (for example after a hot reload) and keeps the console clean.
if (!document.querySelector('script[data-flawsnap]')) {
const s = document.createElement('script')
s.src = 'https://backend.flawsnap.com/widget.js'
s.async = true
s.dataset.key = import.meta.env.VITE_FLAWSNAP_KEY
s.dataset.flawsnap = ''
document.head.appendChild(s)
}Identify the signed-in user
Add the type once:
// flawsnap.d.ts (anywhere your tsconfig includes)
export {}
declare global {
interface Window {
Flawsnap?: {
open(): void
identify(reporter: { name?: string; email?: string }): void
setMetadata(details: Record<string, unknown>): void
screen(name: string): void
track(name: string): void
resetUser(): void
}
}
}Then call identify from your auth state
Put it next to wherever you already know the user, such as an auth provider. The optional chaining means nothing breaks if the widget is blocked or not loaded yet.
useEffect(() => {
if (!user) return
window.Flawsnap?.identify({ name: user.displayName, email: user.email })
window.Flawsnap?.setMetadata({ plan: user.plan, accountId: user.accountId })
}, [user])On sign-out, window.Flawsnap?.resetUser() starts a fresh Insights visit. The name and email from identify only fill in the report form and never reach Insights.
HashRouter and Insights screens
Insights (Pro) groups visits by screen, keyed on the URL path, with ids masked so /orders/123 becomes /orders/:id. With BrowserRouter that works with no code. With HashRouter, every route has the same path, so name screens yourself:
const location = useLocation()
useEffect(() => {
window.Flawsnap?.screen(location.pathname)
}, [location.pathname])Troubleshooting
[flawsnap] widget disabled:onlocalhost: add the dev server host with its port. Vite usually runs onlocalhost:5173; Create React App onlocalhost:3000.identifydoes nothing: it ran before the widget loaded. Optional chaining skipped it. Run it again whenuserchanges, or queue it only while the widget isn’t there yet:const f = window.Flawsnap; if (f?.identify) f.identify({...}); else ((window as any).Flawsnap ??= { q: [] }).q.push(['identify', {...}]).- Your API calls are missing from reports: only the last 100
fetch/XHR requests are kept, and the widget must load before they are made. - Your CSP blocks it: allow
https://backend.flawsnap.cominscript-srcandconnect-src.
Questions
- Is there an npm package for React?
- No. The widget is a hosted script, so there is nothing to install or keep up to date in your
package.json. - Does it work with React Router?
- Yes. The widget follows
pushStatenavigation on its own. Reports carry the current URL, and recordings continue across route changes.
Try it on your own site
7-day free trial, then $9 a month for Starter or $19 for Pro, per organization, with unlimited team members.
Related
Updated