# Framework integration


This page covers using `@flareapp/react`, `@flareapp/vue`, or `@flareapp/svelte` inside an Electron renderer. It follows on from the [quick start](/docs/electron/getting-started/quick-start), which installs `@flareapp/electron` and wires up the main process and the preload script.

In a plain web app, your framework integration 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 your framework's error boundary instead of letting it reach the web singleton.

## Install your framework package

Add your framework's package alongside the one you already installed:

```bash tab=react
npm install @flareapp/react
```

```bash tab=vue
npm install @flareapp/vue
```

```bash tab=svelte
npm install @flareapp/svelte
```

`@flareapp/js` comes in transitively via `@flareapp/electron`, so don't import it in the renderer.

## 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 your framework's error boundary so its reports travel the same path. Two imports make this work: `flare` from `@flareapp/electron/renderer` (the IPC-forwarding instance, no API key, no direct network calls), and the boundary from your framework's `/inject` entry, which takes that instance instead of reaching for a global client.

```tsx tab=react
// App.tsx
import { flare } from '@flareapp/electron/renderer';
import { FlareErrorBoundary } from '@flareapp/react/inject';

export function App() {
    return (
        <FlareErrorBoundary flare={flare}>
            <Root />
        </FlareErrorBoundary>
    );
}
```

```ts tab=vue
// main.ts (renderer entry)
import { createApp } from 'vue';
import { flareVue } from '@flareapp/vue/inject';
import { flare } from '@flareapp/electron/renderer';
import App from './App.vue';

const app = createApp(App);
app.use(flareVue, { flare });
app.mount('#app');
```

```svelte tab=svelte
<script lang="ts">
    import { FlareErrorBoundary } from '@flareapp/svelte/inject';
    import { flare } from '@flareapp/electron/renderer';
</script>

<FlareErrorBoundary {flare}>
    <App />
</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 imports above work on their own.

### React 19 (optional)

On React 19 you can also wire the `createRoot` handlers with the same instance:

```tsx
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 }),
});
```

### Vue: pass the same instance to the component boundary

If you also use the component boundary alongside the plugin, pass the same `flare` instance:

```vue
<script setup lang="ts">
import { FlareErrorBoundary } from '@flareapp/vue/inject';
import { flare } from '@flareapp/electron/renderer';
</script>

<template>
    <FlareErrorBoundary :flare="flare">
        <App />
    </FlareErrorBoundary>
</template>
```

### Svelte: reporting outside a component

To report from an error hook instead of the component boundary, use the handler directly:

```ts
import { createFlareErrorHandler } from '@flareapp/svelte/inject';
import { flare } from '@flareapp/electron/renderer';

const handleError = createFlareErrorHandler({ flare });
```

If you use Flare's Svelte component tracking, point the preprocessor at the `/inject` entry too, so the generated per-component imports stay root-free in the renderer:

```js
// svelte.config.js (renderer build)
import { withFlareConfig } from '@flareapp/svelte/inject';

export default withFlareConfig(
    {
        /* your svelte config */
    },
    { importSource: '@flareapp/svelte/inject' },
);
```

Without `importSource`, the preprocessor emits `import ... from '@flareapp/svelte'`, which pulls the web singleton (and its global side effects) into the renderer.

## Why the two special imports

In a normal web app, your framework integration 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` instance are how you opt out of that web behavior:

- **Import the boundary (and, for React, the error handler) from your framework's `/inject` entry, not its regular entry.** The regular entry quietly falls back to the global web client. The `/inject` entry 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/js` anywhere 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 `flare` instance.** Forget it, and the 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 the `framework` you are using. Your framework's context is included in the IPC call to the main process too, under `context.custom.react` for React, `context.custom.vue` for Vue, and `context.custom.svelte` for Svelte.
