Skip to main content

createClerkBridge(),

Beta

This feature is currently in beta. Functionality may change before general availability. If you run into any issues, please reach out to our support team.

createClerkBridge() creates the Clerk bridge for Electron's main process. It owns Clerk's IPC handlers, token persistence, and the OAuth deep-link transport. Call it before app.whenReady() and before creating any renderer windows, and call the returned cleanup() when tearing down the app.

Usage

src/main.ts
import { createClerkBridge } from '@clerk/electron'
import { storage } from '@clerk/electron/storage'
import { app } from 'electron'

const clerk = createClerkBridge({
  storage: storage(),
  renderer: { scheme: 'my-app', host: 'renderer' },
})

if (clerk.isPrimaryInstance) {
  app.whenReady().then(() => {
    // create your windows
  })
}

app.on('before-quit', () => {
  clerk.cleanup()
})
  • Name
    manageSingleInstanceLock?
    Type
    boolean
    Description

    Whether Clerk should acquire and release Electron's process-wide single-instance lock for OAuth deep links. Defaults to true. Set this to false when the app manages the lock itself. Ignored on macOS, where deep links reach the running instance through open-url.

  • Name
    passkeys?
    Type
    boolean
    Description

    Registers the IPC handlers for native passkey ceremonies. Native support also requires the optional @clerk/electron-passkeys package and exposeClerkBridge({ passkeys: true }). See PasskeysElectron Icon.

  • Name
    renderer?
    Type
    { scheme: string, host: string, privileges?: Electron.Privileges }
    Description

    Registers the custom scheme used to serve the Electron renderer from a stable origin, and enables the OAuth deep-link transport. scheme is a bare scheme name such as my-app (not my-app://) and host is a bare host name such as renderer. The redirect URL Clerk sends to OAuth providers is scheme://host/. Clerk registers the scheme as privileged with { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, stream: true }; values in privileges override those defaults. Required for OAuth and SSO. See Configure OAuth deep links.

  • Name
    storage
    Type
    TokenStorage
    Description

    Storage adapter used by the main process to persist Clerk tokens. Required. Use storage()Electron Icon from @clerk/electron/storage, or provide a custom adapterElectron Icon.

  • Name
    userAgent?
    Type
    string
    Description

    Product token to use in Electron's user-agent fallback, such as Acme Co/1.0.0. Clerk uses the resulting user-agent for <UserProfile /> session activity attribution when no webContents- or session-level user-agent is set. The Electron platform comment is preserved so device details such as macOS or Windows can still be detected.

  • Name
    cleanup
    Type
    () => void
    Description

    Removes IPC handlers and listeners registered by createClerkBridge(), rejects any pending OAuth flow, and releases the single-instance lock if Clerk acquired it.

  • Name
    isPrimaryInstance
    Type
    boolean
    Description

    Whether this process is the one the app should keep booting. false only when Clerk detected a secondary instance on Windows or Linux, forwarded its deep-link arguments to the primary instance, and called app.quit(). Because app.quit() is asynchronous, skip window creation and the rest of your bootstrap when this is false. Always true on macOS.

Errors

createClerkBridge() throws during setup when:

  • storage is missing: Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.
  • renderer.scheme contains : or /: Clerk: renderer.scheme must be a scheme name like "my-app", not a URL or protocol like "my-app://".
  • renderer.host contains : or /: Clerk: renderer.host must be a host name like "renderer", not a URL or origin like "my-app://renderer".

If setup throws partway through, everything already registered is torn down before the error is rethrown.

Custom storage

storage accepts any object with the TokenStorage shape. Methods can be synchronous or return a promise. The SDK currently stores one key, __clerk_client_jwt.

src/token-storage.ts
import type { TokenStorage } from '@clerk/electron'

const memory = new Map<string, string>()

export const memoryStorage: TokenStorage = {
  getItem: (key) => memory.get(key) ?? null,
  setItem: (key, value) => {
    memory.set(key, value)
  },
  removeItem: (key) => {
    memory.delete(key)
  },
}

Single-instance behavior on Windows and Linux

When renderer is set, Clerk calls app.requestSingleInstanceLock() unless the app already holds it. If the lock is unavailable, Clerk calls app.quit() and returns { isPrimaryInstance: false }. If you manage the lock yourself, acquire it before calling createClerkBridge() and pass manageSingleInstanceLock: false:

src/main.ts
const gotTheLock = app.requestSingleInstanceLock()

if (!gotTheLock) {
  app.quit()
} else {
  createClerkBridge({
    storage: storage(),
    renderer: { scheme: 'my-app', host: 'renderer' },
    manageSingleInstanceLock: false,
  })
}

If manageSingleInstanceLock is false and the app doesn't hold the lock, Clerk logs a warning and OAuth callbacks can't be delivered on Windows or Linux until the lock is acquired.

Feedback

What did you think of this content?

Last updated on