Four top-level folders under src/, and one rule that decides which one your code goes in.
The rule
Can this code work without touching the operating system?
Yes: it belongs in the renderer, or in shared. No: it belongs in the main process, reached over an IPC channel.
Reading a file, showing a native dialog, registering a global shortcut, writing to the database, calling an API with a secret key: all main process. Everything a user looks at: renderer.
The folders
src/
main/ Node.js. Windows, files, database, licensing, updates, tray, shortcuts.
preload/ The only bridge between the two. One file. Read it once.
renderer/ Your React app. Screens, hooks, contexts, styles, i18n.
shared/ Types, Zod schemas, and the 45 UI components. Imported by both sides.
features/ One folder per screen. This is where your product lives.Inside main/, folders are named for what they do: db/, licensing/, updater/, telemetry/, security/, services/, ipc/.
The files you will open most
| File | What it decides |
|---|---|
src/renderer/screens-registry.ts |
Which screens exist, in sidebar order |
src/shared/ipc/channels.ts |
Every channel name, in one list |
src/shared/ipc/types.ts |
The request and response shape of every channel |
src/preload/preload.ts |
What the renderer is allowed to call |
src/main/ipc/register.ts |
Which handler answers which channel |
electron-builder.yml |
How the app is packaged and where updates come from |
CLAUDE.md |
The architecture rules, written for AI coding tools |
Adding a feature touches five places
The channel name, the types, the schema, the main handler, and the preload bridge. The generators write four of them:
npm run new:feature MyFeature
npm run new:ipc myfeature:doathingBuild Your First Screen walks the whole thing with a working example.
Why the preload file exists
The renderer runs sandboxed with contextIsolation on. It cannot reach Node.js, which means a compromised page in your app cannot read the user's disk.
The preload script is the one place that decides what crosses that boundary. It exposes a fixed list of functions on window.api and nothing else. If a capability is not in preload.ts, the renderer does not have it. That is the whole security model, and it is worth reading the file once so you know what your renderer can and cannot do.
If you use Claude Code, Cursor, or Copilot
Point it at CLAUDE.md first. It documents these rules, the five-place pattern, and the file-size conventions, so your agent adds code in the right place instead of inventing a new one.