Building Desktop Starter App
A Production-Ready Electron Template for Modern Desktop Applications
The Problem: Starting Electron Apps from Scratch
Every time I started a new Electron project, I found myself solving the same problems over and over:
- Setting up the Electron + React + TypeScript toolchain with proper hot reload
- Configuring the multi-process architecture (main, renderer, preload)
- Implementing auto-updates with electron-updater
- Setting up SQLite with proper WAL mode for performance
- Creating a settings management system
- Configuring cross-platform builds for macOS, Windows, and Linux
- Setting up CI/CD with GitHub Actions
- Implementing window state persistence (size, position, maximized)
- Configuring code signing for macOS notarization
This boilerplate work took 2-3 days before I could write any actual product code. After building StoryFlow, Model Faceoff, and MarkdownFlows, I realized I was copy-pasting the same infrastructure code between projects.
I needed a proper template—not a minimal starter, but production-ready infrastructure.
The Vision: From Clone to App in 5 Minutes
I set out to build Desktop Starter App: an Electron template with all the infrastructure you need for a production desktop application. The core principles:
Zero Configuration
Clone, edit one config file, run npm run dev. Everything works out of the box with sensible defaults.
Production Infrastructure
Auto-updates, SQLite database, settings persistence, and cross-platform builds—not demo features, but battle-tested patterns from real apps.
Single Source of Truth
One app.config.ts file controls everything—app name, bundle ID, GitHub repo, and all build settings. No scattered configs.
CI/CD Ready
GitHub Actions workflows for building on all platforms, code signing for macOS, and automatic GitHub Releases. Push a tag, get binaries.
What's Included: Production-Ready Features
Auto-Updates
electron-updater integration with GitHub Releases. Users get updates automatically with one-click restart.
SQLite Database
better-sqlite3 with WAL mode for high performance. Singleton pattern prevents file locking issues.
Settings Management
Key-value store backed by SQLite. Get, set, and persist any app configuration with type-safe APIs.
Window State
Persists window size, position, and maximized state. Users pick up exactly where they left off.
Cross-Platform
Builds for macOS (DMG), Windows (NSIS), and Linux (DEB). Both Intel and Apple Silicon supported.
Code Signing
macOS notarization ready. Configure your Apple credentials and builds are automatically signed.
The Tech Stack: Modern, Fast, Type-Safe
Electron 38 + React 19 + TypeScript
The latest Electron with React 19's new features. TypeScript throughout—main process, renderer, and preload scripts all fully typed. Catch errors at compile time, not runtime.
Vite for Blazing Fast Development
Electron Forge's Vite plugin provides instant hot module replacement. Three separate Vite configs handle main process, renderer, and preload compilation. Sub-second rebuilds during development.
Tailwind CSS 3.4 + shadcn/ui
Utility-first CSS with Radix UI primitives. Beautiful, accessible components out of the box. Dark mode support included. No CSS-in-JS runtime overhead.
better-sqlite3 with WAL Mode
Synchronous SQLite API with Write-Ahead Logging for concurrent read/write performance. The singleton connection pattern prevents "database is locked" errors that plague many Electron apps.
Vitest for Testing
Vite-native testing with React Testing Library. Fast, parallel test execution. The same config runs your tests as your app, so there are no bundler mismatches.
The Architecture: Electron Done Right
Electron's security model enforces process isolation. The template embraces this:
- Main Process (
src/main.ts): Node.js environment, manages windows, handles auto-updates, initializes database - Renderer Process (
src/renderer/): React application in browser context, communicates viawindow.api - Preload Script (
src/preload.ts): Secure bridge using contextBridge, exposes only safe APIs to renderer
Type-Safe IPC Pattern
Every IPC handler returns a consistent response type:
interface IPCResponse<T> {
success: boolean;
data?: T;
error?: { code: string; message: string };
}
// Usage in renderer
const result = await window.api.settings.get('theme');
if (result.success) {
console.log(result.data); // Type-safe!
}Single Config File
The app.config.ts file is the single source of truth. Change your app name here, and it propagates to package.json, window titles, database paths, and build outputs:
export const config = {
productName: 'Your App Name',
executableName: 'your-app-name',
appBundleId: 'com.yourcompany.yourapp',
appDataFolder: 'YourAppName',
dbFilename: 'app.db',
github: {
owner: 'your-username',
repo: 'your-repo',
private: false,
},
autoUpdateEnabled: true,
};The Database Pattern That Prevents Headaches
SQLite has one critical limitation: file-level locking. Multiple connections cause "database is locked" errors. The template enforces a singleton connection pattern:
// ALWAYS do this
import { getDatabase, generateId } from '../database/connection';
const db = getDatabase();
// NEVER do this
import Database from 'better-sqlite3';
const db = new Database(path); // BREAKS EVERYTHINGThe schema is defined in src/database/schema.ts. Add new tables with idempotent CREATE TABLE statements:
function createYourTable(db: Database.Database): void {
const tableExists = db.prepare(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='your_table'
`).get();
if (!tableExists) {
db.exec(`
CREATE TABLE your_table (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at INTEGER NOT NULL
);
`);
}
}Auto-Updates: Ship Features, Not Installers
The template integrates electron-updater with GitHub Releases. When you push a version tag:
- GitHub Actions builds for all platforms (macOS arm64/x64, Windows, Linux)
- Binaries are uploaded to a GitHub Release (draft by default)
- Publishing the release makes it available to auto-updater
- Running apps check for updates on startup and download in background
- Users are prompted to restart to apply the update
Version management is simple:
# Bump version (creates git tag)
npm run version:patch # 1.0.0 -> 1.0.1
npm run version:minor # 1.0.0 -> 1.1.0
npm run version:major # 1.0.0 -> 2.0.0
# Push tag to trigger release workflow
git push --follow-tagsFor private repositories, add a GH_TOKEN secret. The updater uses it to access release assets.
Quick Start: 5 Minutes to Running App
- 1Clone the repository
git clone https://github.com/dotnetfactory/desktop-starter-app.git my-app - 2Update app.config.ts with your app details
Change productName, appBundleId, and GitHub repository settings
- 3Install dependencies and run
npm install && npm run dev
That's it. You have a working Electron app with hot reload, SQLite database, settings management, and ready for auto-updates. Start building your features.
Apps Built with This Template
This isn't a theoretical template—it's extracted from real production applications:
StoryFlow
AI-powered book writing app with 5-phase workflow
Model Faceoff
Compare 100+ AI models side-by-side
MarkdownFlows
AI-powered Mermaid diagram editor
Every pattern in this template has been validated in production with real users. The auto-update system has shipped dozens of updates. The database pattern has handled gigabytes of local data.
Extending the Template
Adding New IPC Functionality
Three steps to add new main process capabilities:
- Add handler in
src/ipc/handlers.ts - Add API method in
src/preload.ts - Add types in
src/types/window.ts
Adding Database Tables
Add table creation functions in src/database/schema.ts and call them frominitializeDatabase(). Use idempotent patterns—check if table exists before creating.
macOS Code Signing
Set GitHub secrets for APPLE_ID, APPLE_CERTIFICATE, etc. The CI workflow automatically signs and notarizes macOS builds for Gatekeeper compliance.
Technical Highlights
Try the Demo
Download the latest release to see the template in action. Install an older version and watch it auto-update!
Conclusion: Stop Reinventing the Wheel
Desktop Starter App exists because I got tired of solving the same problems on every Electron project. Auto-updates, database setup, settings management, cross-platform builds—these are table stakes for desktop apps, not differentiating features.
This template gives you a 2-3 day head start on any Electron project. The patterns are battle-tested across multiple production applications. The infrastructure just works.
Clone it, customize app.config.ts, and start building what makes your app unique. The boilerplate is handled.
Focus on your product, not on Electron infrastructure.
Tech Stack Summary
Framework: Electron 38, React 19, TypeScript 5.9
Build: Vite 6, Electron Forge 7
Styling: Tailwind CSS 3.4, shadcn/ui, Radix UI
Database: SQLite via better-sqlite3 (WAL mode)
Updates: electron-updater with GitHub Releases
Testing: Vitest, React Testing Library
CI/CD: GitHub Actions with code signing
Platforms: macOS (Intel + Apple Silicon), Windows, Linux
Need Help Building Your Desktop App?
If you need help architecting your Electron application, adding complex features, or scaling your desktop product, let's talk.