Skip to Content
Getting Started

Getting Started

Create your app

Use degit  to copy the template without the git history of this repository:

npx degit nick-keller/electron-appstack/template my-app cd my-app

Install dependencies

Requires Node.js 22.12 or later.

npm install

The postinstall script downloads the Electron binary for your platform and prepares native modules (SQLite) for Electron.

The template ships a package-lock.json and its GitHub workflows use npm ci. To use another package manager, delete the lockfile and update the workflows.

Start the app

npm run dev

An Electron window opens with a small todo list (stored in SQLite) and a settings page (theme and language). See Development for how hot reload works.

Make it yours

  • Rename the app: name and productName in package.json, appId and publish in electron-builder.ts, <title> in src/renderer/index.html (see What each name controls)
  • Replace the icon and DMG background in build/ (see Icons and Images)
  • Set up the release secrets (see Releasing)

What each name controls

The app name is set in several places, and each one shows up somewhere different:

SettingFileUsed for
productNamepackage.jsonThe name people see: app.getName(), the macOS app menu and Dock, the .app bundle and .exe names, the Windows Start Menu shortcut and “Installed apps” entry, the Linux desktop entry, installer file names, and the data folder
namepackage.jsonThe npm package name, used when productName is missing. On Linux, also the executable and .deb package name. Lowercase, no spaces
<title>src/renderer/index.htmlThe window title: the title bar, the Windows and Linux taskbar button, the macOS Window menu and Mission Control
appIdelectron-builder.tsThe unique ID of the app: the macOS bundle identifier (used for code signing, notarization and permissions) and the Windows app ID (used for taskbar grouping, notifications and the uninstaller entry). Reverse domain, e.g. com.acme.notes
publish.owner, publish.repoelectron-builder.tsThe GitHub repository where releases are uploaded and where the auto updater looks for new versions
copyrightelectron-builder.tsThe macOS Get Info window and the Windows file properties
description, authorpackage.jsonPackage metadata, shown by Linux package managers
appNamesrc/shared/locales/*/common.jsonA translated name for your own UI, used by the license activation window

The window title only comes from <title>, not from productName. Keep them in sync, or set the title from code with document.title in the renderer.

In development, the app runs inside the Electron binary: the macOS menu bar, Dock and app switcher show “Electron” with the Electron icon. Run npm run dist:dir to see the real name and icon.

The data folder

The database and the config file are stored in app.getPath('userData'), a folder named after productName:

PlatformFolder
macOS~/Library/Application Support/<productName>
Windows%APPDATA%\<productName>
Linux~/.config/<productName>

Choose productName and appId before your first release. Changing productName later moves the data folder, so existing users start with an empty database and default settings. Changing appId makes the new version a different app to the operating system: on Windows it installs next to the old one instead of replacing it, and on macOS users are asked again for permissions such as notifications.

Remove the todo example

The todo list shows how the database, tRPC and TanStack Query fit together. To start from a clean slate, give this prompt to your coding agent:

Remove the todo example from this app so I can start from a clean slate: - Delete `src/main/trpc/routers/todos.ts` and remove `todosRouter` from `src/main/trpc/router.ts`. - In `src/main/db/schema.ts`, delete the `todos` table and the imports it used. Keep the comment. - Delete `drizzle/0000_init.sql` and `drizzle/meta/0000_snapshot.json`, and set `entries` to `[]` in `drizzle/meta/_journal.json`. My first table will get a fresh migration with `npm run db:generate`. - Replace `src/renderer/src/routes/index.tsx` with an empty home page: keep the route, render a `Card` with `t('home.title')` and nothing else. Remove unused imports. - In every locale in `src/shared/locales/`, set `home.title` to "Home" (translated) and delete the other `home.*` keys. - In `AGENTS.md` and `src/renderer/src/lib/trpc.ts`, replace the `trpc.todos.*` examples with a generic `trpc.items.*`. - Run `npm run db:reset`, `npm run typecheck` and `npm run check:fix`, and fix any errors.

Scripts

ScriptDescription
npm run devStart the app in development mode, with hot reload
npm run buildType check and build the app into out/
npm run previewRun the production build locally
npm run typecheckType check the main, preload and renderer code
npm run checkLint and check formatting with Biome
npm run check:fixFix lint and formatting issues
npm run lintLint only
npm run formatFormat only
npm run db:generateGenerate a SQL migration from the database schema
npm run db:resetDelete the development database
npm run distBuild installers for the current platform into dist/
npm run dist:dirBuild an unpacked app into dist/, faster, for testing
npm run changesetDescribe a change for the next release
Last updated on