Back to Home

Building Desktop Starter App

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:

  1. Setting up the Electron + React + TypeScript toolchain with proper hot reload
  2. Configuring the multi-process architecture (main, renderer, preload)
  3. Implementing auto-updates with electron-updater
  4. Setting up SQLite with proper WAL mode for performance
  5. Creating a settings management system
  6. Configuring cross-platform builds for macOS, Windows, and Linux
  7. Setting up CI/CD with GitHub Actions
  8. Implementing window state persistence (size, position, maximized)
  9. 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 via window.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 EVERYTHING

The 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:

  1. GitHub Actions builds for all platforms (macOS arm64/x64, Windows, Linux)
  2. Binaries are uploaded to a GitHub Release (draft by default)
  3. Publishing the release makes it available to auto-updater
  4. Running apps check for updates on startup and download in background
  5. 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-tags

For private repositories, add a GH_TOKEN secret. The updater uses it to access release assets.

Quick Start: 5 Minutes to Running App

  1. 1
    Clone the repository
    git clone https://github.com/dotnetfactory/desktop-starter-app.git my-app
  2. 2
    Update app.config.ts with your app details

    Change productName, appBundleId, and GitHub repository settings

  3. 3
    Install 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:

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:

  1. Add handler in src/ipc/handlers.ts
  2. Add API method in src/preload.ts
  3. 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

Electron 38 with latest security features
React 19 + TypeScript 5.9
Vite 6 for instant hot reload
Tailwind CSS 3.4 + shadcn/ui components
better-sqlite3 with WAL mode
electron-updater for auto-updates
electron-forge for packaging
GitHub Actions CI/CD pipeline
macOS notarization ready
Cross-platform builds (macOS, Windows, Linux)

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.