Configuration
This guide covers using @flareapp/react inside an Electron renderer. If you haven't set up the core Electron client yet, start with how the Electron client works.
In a plain web app, @flareapp/react reports through the @flareapp/js singleton you configure with flare.light(key). In Electron that is wrong: the API key must live in the main process only, and reports must travel over IPC so main can enrich and gate them. So in the renderer you inject Electron's own Flare instance into the React boundary instead of letting it reach the web singleton.
Install
npm install @flareapp/electron @flareapp/react
@flareapp/js comes in transitively via @flareapp/electron, so don't import it in the renderer.
1. Main process: owns the key and the transport
// main.ts
import { app } from 'electron';
import { flare } from '@flareapp/electron/main';
app.whenReady().then(() => {
flare.light('YOUR PROJECT KEY'); // key lives ONLY here
});
2. Preload: bridges the renderer to main
// preload.ts
import { exposeFlare } from '@flareapp/electron/preload';
exposeFlare(); // exposes window.__flare.report() via contextBridge
Point your BrowserWindow at this preload with contextIsolation: true (the default).
3. React: report through the Electron instance
In the renderer, @flareapp/electron/renderer gives you a Flare instance that forwards every report to the main process over IPC. Hand that instance to React's error boundary so React reports travel the same path. Two imports make this work:
flarefrom@flareapp/electron/rendereris the IPC-forwarding instance (no API key, no direct network calls).FlareErrorBoundaryfrom@flareapp/react/injectis a build of the boundary that takes aflareprop instead of reaching for a global client.
// App.tsx
import { flare } from '@flareapp/electron/renderer';
import { FlareErrorBoundary } from '@flareapp/react/inject';
export function App() {
return (
<FlareErrorBoundary flare={flare}>
<Root />
</FlareErrorBoundary>
);
}
If you pass flare around in several places, re-export it from a small local module (e.g. renderer/flare.ts) so you import it from one spot. That is optional sugar; the import above works on its own.
On React 19 you can also wire the createRoot handlers with the same instance:
import { flare } from '@flareapp/electron/renderer';
import { flareReactErrorHandler } from '@flareapp/react/inject';
import { createRoot } from 'react-dom/client';
createRoot(document.getElementById('root')!, {
onCaughtError: flareReactErrorHandler({ flare }),
onUncaughtError: flareReactErrorHandler({ flare }),
});
Why the two special imports
In a normal web app, @flareapp/react and @flareapp/js share one global Flare client: it holds your API key and posts reports straight to Flare. That is exactly what you want in a browser, and exactly what you do not want in an Electron renderer, where the key has to stay in the main process and reports have to go over IPC.
The /inject entry and the explicit flare prop are how you opt out of that web behavior:
- Import the boundary and handler from
@flareapp/react/inject, not@flareapp/react. The regular entry quietly falls back to the global web client. The/injectentry has no global to fall back to, so it can only report through the instance you give it. (Import the regular entry by accident and Flare logs a console warning, so you can catch the mistake.) - Don't import
@flareapp/jsanywhere in the renderer. It would start a second global error listener and try to send reports directly to Flare with no API key, bypassing the main process entirely. - Always pass the
flareprop. Forget it and the error boundary throws the moment your app boots, not later when an error happens, so a missing instance is obvious right away instead of silently dropping reports.
Once wired, reports from the renderer carry sdk = @flareapp/electron and framework = React. Your React context (context.custom.react, the component stack) gets included in the IPC call to the main process.