Clerk Electron SDK, Beta
The Clerk Electron SDK gives you access to Clerk's prebuilt components, React hooks, and helpers inside an Electron app. Refer to the quickstart to get started.
Patch releases can still change behavior; check the changelog when you upgrade.
How the SDK fits Electron's process model
Electron runs your app in separate processes, and the SDK ships one entrypoint for each. Install @clerk/electron and the electron-store@8 peer dependency used by the default storage adapter, then import from the entrypoint that matches the process.
npm install @clerk/electron electron-store@8pnpm add @clerk/electron electron-store@8yarn add @clerk/electron electron-store@8bun add @clerk/electron electron-store@8Clerk loads its prebuilt UI from your instance's at runtime rather than from your app's bundle, so the renderer's Content Security Policy () must allow that host, and the components don't render while the app is offline.
Authentication options
Some sign-in methods depend on how the app is running. In development, Electron Forge serves the renderer from Vite's dev server. In a packaged app, the renderer is served from your custom scheme (for example, my-app://renderer), which is what OAuth and SSO callbacks return to. See Configure OAuth deep links.
"Not verified" means the method hasn't been exercised with the Electron SDK yet. SMS + OTP and SAML use the same transports as Email + OTP and OAuth respectively. If you rely on one of these, test it in your app and contact support with feedback.
Passkeys in a local bundle or on the dev server need native mode, a separate implementation (an IPC bridge to the OS through the optional native module) with its own requirements. The renderer's built-in WebAuthn only works when the window loads https:// content on an origin that matches your instance's RP ID. See Configure passkeys.
Email links open in the user's browser, not in your app, so they can't complete a sign-in that started in Electron. Google One Tap requires a web origin that Google recognizes, and Web3 requires a wallet injected into the page; neither exists in an Electron renderer.
What the SDK doesn't include
- Server-side helpers. There's no
auth(),clerkMiddleware(), or Backend API client. The main process stores tokens and talks to the OS; it isn't a server. Call your own backend, and verify Clerk session tokens there with the Backend SDK. - Non-React renderers. The only renderer integration is
@clerk/electron/react. A plain@clerk/clerk-jsinstance works for passkeys, but is otherwise undocumented. - Bundled UI. The
uiprop isn't accepted; the UI always loads from your Frontend API host.
Requirements
- Electron 28 or later and Node.js 20.9 or later.
contextIsolationenabled (Electron's default).nodeIntegrationis never required in the renderer.- Clerk's IPC only answers a
BrowserWindow's main frame.<webview>tags,BrowserViews, and iframes can't use Clerk even if they load the preload script. - The Native API enabled on your instance. In the Clerk Dashboard, navigate to the Native applications page. Every origin your renderer runs on (the dev server, your custom scheme) must also be in the instance's
allowed_origins. Requests that carry both anOriginand anAuthorizationheader are rejected from other origins.
Frequently asked questions (FAQ)
Why does OAuth open the system browser?
The renderer runs on a custom scheme rather than a web origin, so a same-window redirect can't complete the provider's flow. The SDK opens the provider in the system browser and receives the callback through a deep link, the same pattern Clerk's mobile SDKs use. This is why OAuth needs a packaged app on macOS and Linux.
Why did my app become single-instance on Windows and Linux?
On those platforms a deep link starts a second copy of the app. createClerkBridge() takes Electron's single-instance lock so the callback reaches the copy that's already running, then quits the new one. Pass manageSingleInstanceLock: false if your app already holds the lock; see createClerkBridge().
Why does every request fail with "Setting both the 'Origin' and 'Authorization' headers is forbidden"?
Your renderer's origin isn't in the instance's allowed_origins. Add the dev-server origin (http://localhost:5173 with Electron Forge) and your packaged app's scheme origin (my-app://renderer) with the Backend API as shown in the quickstart and the OAuth deep links guide.
Why does my packaged app start signed out after every relaunch?
Electron's safeStorage couldn't use OS-level encryption, so the SDK kept the session token in memory only. On macOS, a common cause is an unsigned or ad-hoc-signed packaged app; on Linux, the machine might not have a keyring. See the storage() behavior, and package and sign your app on macOS.
Why do Clerk's components fail to load?
Almost always a CSP that doesn't allow your Frontend API host in script-src. The renderer console shows:
Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.See the quickstart's CSP step.
Is my Secret Key bundled into the app?
No. The app ships only your and Frontend API host, and both are public by design. Your is used once, from your own machine, to allowlist the renderer's origin, and it never belongs in the renderer or the main process. Anything that needs the Secret Key, such as calling the Backend API, belongs on a server your app talks to.
Can a page loaded in my window reach the Clerk bridge?
Only if you let it. The preload script runs in every document the window loads, so a page from another origin would inherit the bridge and Clerk's token cache. The quickstart's navigation guard keeps the window on your renderer's origin and opens every other link in the system browser. Keep it in place.
Feedback
Last updated on