How it works
The @flareapp/electron package is the Flare client for Electron apps. It captures JavaScript errors in both Electron processes (main and renderer) and routes every report through the main process, so your API key lives in exactly one place.
If you render your UI with React, Vue, or Svelte, see the framework-specific configuration after you have the core setup in place:
- Using Flare with Electron + React
- Using Flare with Electron + Vue
- Using Flare with Electron + Svelte
What it captures
- Main process: uncaught exceptions and unhandled promise rejections.
- Renderer process:
window.onerrorandunhandledrejection, plus anything you report manually. - Process crashes:
render-process-goneandchild-process-gone(renderer/GPU/utility), reported as structured errors with the crashreasonandexitCode.
It does not capture native crashes (C++/Crashpad minidumps). Only JavaScript-level errors are sent to Flare.
Installation
npm install @flareapp/electron
electron is a peer dependency; this package expects your app to provide it.
Setup
Flare needs wiring in all three Electron contexts. The API key, stage, version, and sourcemap settings are configured once, in the main process. The renderer needs none of them.
1. Main process
// main.ts
import { app } from 'electron';
import { flare } from '@flareapp/electron/main';
flare.light('your-flare-api-key');
// Optional: these are set ONCE here and applied to renderer reports too.
flare.configure({ stage: 'production', version: app.getVersion() });
// Optional: fatal-handler behavior and IPC trust policy.
flare.configureElectron({
uncaughtExceptionMode: 'report-and-exit', // 'report' | 'report-and-exit' | 'off'
unhandledRejectionMode: 'report-and-exit',
captureRenderProcessGone: true,
});
In report-and-exit mode, after a fatal error Flare reports it, flushes pending reports (up to shutdownTimeoutMs), then calls app.exit(1).
2. Preload script
Because contextIsolation is on (the Electron default and the recommended setting), the renderer cannot reach ipcRenderer directly. The preload helper bridges reports over contextBridge. This step is required. Without it, renderer reports are dropped.
// preload.ts
import { exposeFlare } from '@flareapp/electron/preload';
exposeFlare();
Make sure your BrowserWindow points at this preload script:
new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
},
});
3. Renderer
Import the renderer entry once, as early as possible, to install the global error listeners:
// renderer entry, e.g. main.tsx / index.ts
import '@flareapp/electron/renderer';
For manual reporting, use the exported instance:
import { flare } from '@flareapp/electron/renderer';
try {
doRiskyThing();
} catch (error) {
flare.report(error as Error);
}
The renderer builds the full report (stack trace + source snippets + browser context) in its own context, then forwards it to the main process. No API key lives in the renderer.
How reports flow
renderer error
→ RendererFlare builds Report (stack + snippets + browser context)
→ renderer beforeSubmit → serialize → size-check
→ window.__flare.report(jsonString) [contextBridge]
→ ipcRenderer.invoke('flare:report') [IPC]
→ main: trust sender → size-check → parse → validate
→ overlay stage/version/sourcemap + app metadata + user
→ main beforeSubmit → sent to Flare
main-process error
→ process handlers → sent to Flare (app.exit on report-and-exit)
renderer / GPU crash
→ render-process-gone / child-process-gone → reported → sent to Flare
The API key is held only in the main process because that is the single egress point: every report, wherever it originates, is sent from main.
Filtering reports (beforeSubmit)
beforeSubmit runs in two stages: once in the renderer (scrub close to the source) and once in main (the final gate before sending). Returning null/false from either drops the report.
// main
flare.configure({
beforeSubmit: (report) => {
// final scrub before sending
return report;
},
});
// renderer
import { flare } from '@flareapp/electron/renderer';
flare.configure({
beforeSubmit: (report) => {
delete report.attributes['context.custom'];
return report;
},
});
Sender trust
The main process only accepts reports from frames it trusts. By default it accepts:
file:URLs (packaged builds), andhttp(s)onlocalhost/127.0.0.1(dev servers).
It rejects everything else, including remote origins and custom protocols. If your app serves its renderer over a custom protocol or loads trusted remote content, opt in:
// Add a custom protocol scheme:
flare.configureElectron({ trustedProtocols: ['app'] });
// Or take full control:
flare.configureElectron({
trustSender: (frame) => new URL(frame.url).origin === 'https://app.example.com',
});
Attaching the current user
import { flare } from '@flareapp/electron/main';
flare.setUser({ id: 123, email: '[email protected]', username: 'jane' });
flare.setUser(null); // clear on logout
The user is attached to main-process reports and to forwarded renderer reports.
Not captured
- Native crashes / Crashpad minidumps.
- Errors that occur before
flare.light('your-key')runs in the main process. The fatal process handlers are attached bylight(), and no report is sent without a key, so calllight()as early as possible in your main entry. Errors before that point (in any process) are not sent.
Using a UI framework
When your renderer uses React, Vue, or Svelte, the framework integration must report through Electron's Flare instance (over IPC) instead of the @flareapp/js web singleton. Each framework has a dedicated /inject entry for this: