Skip to content

Getting Started

electron-trpc lets the renderer processes of your Electron app call a tRPC router in the main process, over Electron’s IPC.

  • Electron 35 or later
  • tRPC 11.10 or later
  • Context isolation, which Electron enables by default
Terminal window
pnpm add electron-trpc @trpc/server @trpc/client

You’ll need TypeScript to work across your main and renderer code to get inferred types in the renderer. The examples show setups with Vite and with webpack.

  1. Define a tRPC router in the main process, as you would for any tRPC server:

    api.ts
    import { EventEmitter, on } from 'node:events';
    import { initTRPC } from '@trpc/server';
    import z from 'zod';
    const ee = new EventEmitter();
    const t = initTRPC.create({ isServer: true });
    export const router = t.router({
    greeting: t.procedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => `Hello ${input.name}`),
    greet: t.procedure.input(z.object({ name: z.string() })).mutation(({ input }) => {
    ee.emit('greeting', `Greeted ${input.name}`);
    }),
    greetings: t.procedure.subscription(async function* ({ signal }) {
    for await (const [text] of on(ee, 'greeting', { signal })) {
    yield text as string;
    }
    }),
    });
    export type AppRouter = typeof router;
  2. Create an IPC handler for the router in the main process. It also registers electron-trpc’s preload script, so create it before any window loads a page:

    main.ts
    import { app, BrowserWindow } from 'electron';
    import { createIPCHandler } from 'electron-trpc/main';
    import { router } from './api';
    app.whenReady().then(() => {
    createIPCHandler({ router });
    const win = new BrowserWindow();
    win.loadFile('index.html');
    });

    The handler answers requests from all of your app’s windows.

  3. Create a tRPC client in the renderer with ipcLink, which takes the place of the HTTP and WebSocket links:

    renderer.ts
    import { createTRPCClient } from '@trpc/client';
    import { ipcLink } from 'electron-trpc/renderer';
    import type { AppRouter } from './api';
    export const client = createTRPCClient<AppRouter>({
    links: [ipcLink()],
    });
    const greeting = await client.greeting.query({ name: 'Electron' });

From here, use the client however you would with tRPC, including with TanStack React Query.

  • Preload script: how electron-trpc reaches your pages, and what to do if your build can’t include its preload script.
  • Request lifetimes: when requests and subscriptions end.
  • Security: limiting which pages can call your router.