Skip to content

A production-ready Electron starter template featuring React 19, TypeScript, Tailwind CSS, and a modular architecture powered by @devisfuture/electron-modular

Notifications You must be signed in to change notification settings

trae-op/electron-modular-boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

53 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Electron Modular Boilerplate

A production-ready Electron starter template featuring React 19, TypeScript, Tailwind CSS, and a modular architecture powered by @devisfuture/electron-modular. This boilerplate comes with pre-configured OAuth authentication (Google, GitHub), auto-update logic, comprehensive unit testing, and ready-to-use AI Agent documentation for GitHub Copilot.

App build


🎯 Project Overview

This is a full-featured starter kit designed to accelerate Electron application development. Whether you're building a desktop app from scratch or migrating an existing project, this boilerplate provides:

  • Modular architecture with Dependency Injection (DI) pattern
  • Production-ready authentication with OAuth 2.0 (Google, GitHub)
  • Auto-update system using electron-updater
  • Type-safe IPC communication between renderer and main processes
  • Modern React stack with hooks, context patterns, and virtualized lists
  • Comprehensive testing setup with Vitest
  • CI/CD pipeline with GitHub Actions
  • AI-friendly documentation optimized for GitHub Copilot

Demo: Reusable authentication and custom UI

auth-ui

Auto-update Demo

auto-update

πŸš€ Features

Core Technologies

Frontend (Renderer Process)

  • βš›οΈ React 19 - Latest React with concurrent features
  • 🎨 Tailwind CSS 3.4 - Utility-first CSS framework
  • 🧭 React Router DOM 7 - Hash-based routing for Electron
  • πŸ“¦ Vite 6 - Lightning-fast development server and build tool
  • 🎭 Lucide React - Beautiful icon library
  • πŸ“Š React Window - Virtualized lists for performance
  • πŸ”„ React Virtualized Auto Sizer - Auto-sizing for virtualized components

Backend (Main Process)

  • πŸ”Œ Electron 38 - Latest Electron with modern APIs
  • πŸ—οΈ @devisfuture/electron-modular - Dependency Injection framework
  • πŸ” OAuth 2.0 Authentication - Google, GitHub
  • πŸ“‘ Axios - HTTP client for REST API calls
  • πŸ’Ύ Electron Store - Persistent storage with encryption support
  • πŸ“ Electron Log - Production-grade logging
  • ⬆️ Electron Updater - Auto-update functionality
  • πŸ”§ Electron Builder - Multi-platform builds (Windows, macOS, Linux)

Development Tools

  • πŸ“˜ TypeScript 5.7 - Strict type checking
  • βœ… ESLint 9 - Code linting with TypeScript support
  • πŸ’… Prettier 3.7 - Code formatting with import sorting
  • πŸ§ͺ Vitest 3 - Fast unit testing framework
  • 🎭 Testing Library - React component testing utilities
  • πŸ“¦ PostCSS + Autoprefixer - CSS processing

πŸ—οΈ Architecture

Main Process Architecture

The main process uses a modular architecture with Dependency Injection:

src/main/
β”œβ”€β”€ app.ts                    # Application entry point
β”œβ”€β”€ config.ts                 # Global configuration
β”œβ”€β”€ preload.cts              # Preload script for IPC bridge
β”œβ”€β”€ @shared/                 # Shared utilities
β”‚   β”œβ”€β”€ store.ts            # State management (Map + electron-store)
β”‚   β”œβ”€β”€ logger.ts           # Logging utilities
β”‚   β”œβ”€β”€ ipc/                # IPC type-safe helpers
β”‚   └── error-messages.js   # Error notification system
β”œβ”€β”€ app/                     # Main application module
β”‚   β”œβ”€β”€ module.ts           # Module registration
β”‚   β”œβ”€β”€ service.ts          # Business logic
β”‚   β”œβ”€β”€ ipc.ts             # IPC handlers
β”‚   └── window.ts          # Window manager
β”œβ”€β”€ auth/                    # OAuth authentication module
β”‚   β”œβ”€β”€ module.ts
β”‚   β”œβ”€β”€ service.ts         # Auth logic (logout, storage cleanup)
β”‚   β”œβ”€β”€ ipc.ts            # Auth IPC handlers
β”‚   └── window.ts         # OAuth popup window manager
β”œβ”€β”€ user/                    # User data module
β”‚   β”œβ”€β”€ module.ts
β”‚   └── service.ts         # User API calls
β”œβ”€β”€ rest-api/                # HTTP client module
β”‚   β”œβ”€β”€ module.ts
β”‚   └── service.ts         # Axios wrapper with caching
β”œβ”€β”€ updater/                 # Auto-update module
β”‚   β”œβ”€β”€ module.ts
β”‚   β”œβ”€β”€ services/          # Update logic for Windows/macOS
β”‚   └── window.ts          # Update notification window
β”œβ”€β”€ notification/            # System notifications
β”œβ”€β”€ menu/                    # Application menu
└── tray/                    # System tray icon

Key Concepts:

  • Each feature is a self-contained module with clear responsibilities
  • Services handle business logic and are auto-injected via @Injectable()
  • IPC Handlers manage renderer ↔ main communication with @IpcHandler()
  • Window Managers control window lifecycle with @WindowManager()
  • Tokens enable custom dependency injection

Renderer Process Architecture

The renderer follows a domain-driven design with React:

src/renderer/
β”œβ”€β”€ App.tsx                 # App shell with providers + router
β”œβ”€β”€ main.tsx                # React entry point
β”œβ”€β”€ components/             # Reusable UI primitives
β”‚   β”œβ”€β”€ Button/
β”‚   β”œβ”€β”€ IconButton/
β”‚   β”œβ”€β”€ Avatar/
β”‚   β”œβ”€β”€ Popover/
β”‚   β”œβ”€β”€ List/
β”‚   └── TextField/
β”œβ”€β”€ composites/             # Cross-cutting feature blocks
β”‚   β”œβ”€β”€ Routes/            # Public/Private route guards
β”‚   β”œβ”€β”€ LightDarkMode/     # Theme toggle
β”‚   β”œβ”€β”€ LazyRender/        # Virtualized list renderer (react-window + AutoSizer) - renders only visible items for large collections
β”‚   └── AppVersion/        # Version display
β”œβ”€β”€ conceptions/            # Domain modules (feature packages)
β”‚   β”œβ”€β”€ Auth/              # Authentication
β”‚   β”‚   β”œβ”€β”€ Context/       # Auth state (useSyncExternalStore pattern)
β”‚   β”‚   β”œβ”€β”€ components/    # SignIn, ProviderButton
β”‚   β”‚   └── hooks/         # useControl, useSelectors
β”‚   β”œβ”€β”€ User/              # User profile
β”‚   β”‚   β”œβ”€β”€ Context/       # User state
β”‚   β”‚   β”œβ”€β”€ components/    # UserPopover, Avatar
β”‚   β”‚   └── hooks/         # User data hooks
β”‚   └── Updater/           # Update UI
β”‚       β”œβ”€β”€ Context/       # Update state
β”‚       └── components/    # UpdateNotification
β”œβ”€β”€ layouts/                # Page layouts
β”‚   β”œβ”€β”€ Main.tsx
β”‚   └── TopPanel.tsx
└── windows/                # Route pages
    β”œβ”€β”€ Home/
    └── Settings/

Key Patterns:

  • Context Pattern with useSyncExternalStore - Optimized state management without unnecessary re-renders
  • Subscription-based state - Components subscribe to specific state slices
  • Domain modules (conceptions) - Feature-complete packages with state + UI + hooks
  • Separation of concerns - UI primitives, composites, and domain logic are clearly separated

πŸ” OAuth Authentication Flow

This project implements a complete OAuth 2.0 flow with support for multiple providers.

Supported Providers

  • βœ… Google OAuth 2.0
  • βœ… GitHub OAuth

How It Works

  1. User clicks "Sign In with Google/GitHub" in the renderer

  2. Renderer sends IPC message windowAuth with provider type

  3. Main process opens OAuth popup window (AuthWindow)

    // src/main/auth/window.ts
    @WindowManager<TWindows["auth"]>({
      hash: "window:auth",
      options: { width: 400, height: 400, sandbox: true }
    })
  4. Popup navigates to provider OAuth URL

    GET {BASE_REST_API}/api/auth/google
    GET {BASE_REST_API}/api/auth/github
    
  5. Backend handles OAuth flow:

    • Redirects to Google/GitHub authorization page
    • User grants permissions
    • Provider redirects back with authorization code
    • Backend exchanges code for access token
    • Backend fetches user profile from provider API
    • Backend creates/updates user in database
    • Backend redirects to: {APP_URL}/api/auth/verify?token={JWT}&userId={ID}
  6. AuthWindow intercepts redirect via onWebContentsWillRedirect:

    const isVerify = /api\/auth\/verify\?token\=/g.test(url);
    if (isVerify) {
      const token = searchParams.get("token");
      const userId = searchParams.get("userId");
    
      // Store credentials
      setElectronStorage("authToken", token);
      setElectronStorage("userId", userId);
    
      // Notify renderer
      ipcWebContentsSend("auth", mainWindow.webContents, {
        isAuthenticated: true,
      });
    
      // Close popup
      this.window?.close();
    }
  7. Renderer updates auth state and redirects to authenticated routes

REST API Integration

The boilerplate uses a custom RestApiService with:

  • Axios instance with base URL from .env
  • Response caching using electron-store
  • Token-based authentication with Bearer tokens
  • Error handling with 401 redirect to logout

User Data Fetching

// src/main/user/service.ts
async byId<R extends TUser>(id: string): Promise<R | undefined> {
  const response = await this.restApiProvider.get<R>(
    `${restApi.urls.base}${restApi.urls.baseApi}${restApi.urls.user.base}/${id}`,
    {
      headers: {
        Authorization: `Bearer ${getElectronStorage("authToken")}`
      },
      isCache: true
    }
  );

  // Auto-logout on 401
  if (response.error?.details?.statusCode === 401) {
    this.authProvider.logout(mainWindow);
    return;
  }

  return response.data;
}

API Endpoints Used

Method Endpoint Purpose
GET /api/auth/google Initiate Google OAuth flow
GET /api/auth/github Initiate GitHub OAuth flow
GET /api/auth/verify?token=&userId= Callback with JWT token
GET /api/user/{userId} Fetch user profile (requires Bearer token)

πŸ”„ Auto-Update System

Built-in auto-update functionality using electron-updater:

  • Automatic update checks on app launch
  • Background downloads with progress tracking
  • GitHub Releases integration - fetches updates from repository releases
  • Platform-specific implementations:
    • Windows: NSIS installer with differential downloads (auto-update works on Windows without code signing)
    • macOS: DMG with code signing support. Important: macOS requires code signing and notarization for standard auto-update via the ecosystem. This project includes a custom macOS auto-update implementation (using built-in Node.js modules) so an update workflow can work without electron-builder code signing if you need it. If you prefer the standard signed macOS flow, use electron-builder and configure macOS code signing/notarization.
    • Linux: AppImage
  • Update notifications with system tray integration
  • Manual update checks via application menu

Code signing notes πŸ”

  • Windows: We use electron-builder for Windows builds. Windows auto-update can work without code signing, so signing is optional; however, if you want signed installers, configure a Windows code signing certificate and set it in your electron-builder configuration.
  • macOS: Official macOS auto-update and distribution typically requires proper code signing and notarization. This boilerplate provides a custom macOS auto-update (not relying on electron-builder) to support update flows without signing. If you need code-signed macOS builds and standard auto-update behavior, configure electron-builder and supply the appropriate Apple Developer certificates and notarization settings.

πŸ“‘ Type-Safe IPC Communication

All IPC communication is fully typed with a single API:

IPC Channels

// Renderer β†’ Main (fire-and-forget)
window.electron.send("send", {
  type: "windowAuth",
  data: { provider: "google" },
});

// Renderer β†’ Main (request/response)
const version = await window.electron.invoke("invoke", {
  type: "getAppVersion",
});

// Main β†’ Renderer (push events)
ipcWebContentsSend("auth", webContents, { isAuthenticated: true });

Type Definitions

All IPC types are defined globally in types/:

// types/sends.d.ts
type TEventPayloadSend = {
  windowAuth: { provider: TProviders };
  windowUpdateApp: undefined;
  // ...
};

// types/invokes.d.ts
type TEventPayloadInvoke = {
  getAppVersion: undefined;
  getUser: { id: string };
  // ...
};

// types/receives.d.ts
type TEventPayloadReceive = {
  auth: { isAuthenticated: boolean };
  updater: TUpdaterPayload;
  // ...
};

πŸ§ͺ Testing

Comprehensive unit testing setup with Vitest:

Test Coverage

  • βœ… Main process tests - All services, IPC handlers, window managers
  • βœ… Renderer tests - React components, hooks, contexts
  • βœ… Mocked dependencies - Electron APIs, stores, IPC

Running Tests

# Run all tests
npm run test:unit:renderer
npm run test:unit:main

# Watch mode (development)
vitest src/renderer --watch
vitest --config vitest.config.main.ts --watch

Test Structure

src/main/
└── auth/
    β”œβ”€β”€ service.ts
    β”œβ”€β”€ service.test.ts      # Unit tests for AuthService
    β”œβ”€β”€ ipc.ts
    └── ipc.test.ts          # Unit tests for IPC handlers

src/renderer/
└── conceptions/
    └── Auth/
        β”œβ”€β”€ hooks/
        β”‚   β”œβ”€β”€ useControl.ts
        β”‚   └── useControl.test.ts
        └── Context/
            β”œβ”€β”€ Context.tsx
            └── Context.test.tsx

πŸ“š AI Agent Documentation

The docs/ folder contains comprehensive guides optimized for GitHub Copilot:

Document Description
typescript.md TypeScript best practices (use type instead of interface, naming conventions)
javascript.md Modern JavaScript patterns, performance optimization, algorithms
react.md React component patterns, custom hooks, props typing
сontext-pattern.md Context pattern with useSyncExternalStore for optimal re-renders
main-process-modular-architecture.md Electron main process DI architecture
renderer-process-architecture.md Renderer domain-driven design
ipc-communication.md Type-safe IPC patterns
tailwind-css.md Tailwind utility patterns
clsx-tailwind.md Conditional className composition
lucide-react.md Icon usage guidelines
event-delegation-guide.md Event delegation patterns
react-form-instructions.md Form handling best practices
main-process-modular-unit-tests.md Testing main process modules
renderer-process-unit-tests.md Testing React components
electron-path-aliasing.md Import path aliases configuration
git-commit-instructions.md Commit message conventions

These docs help AI agents (like GitHub Copilot) understand project patterns and generate consistent, high-quality code.


πŸ“¦ Installation

Prerequisites

  • Node.js 22.x or higher
  • npm 10.x or higher
  • Git

Step 1: Clone the Repository

git clone https://github.com/trae-op/electron-modular-boilerplate.git
cd electron-modular-boilerplate

Step 2: Install Dependencies

npm install

Step 3: Configure Environment Variables

Create .env file in the root directory:

# REST API Base URL (your backend server)
BASE_REST_API=http://localhost:3000

# Development mode (automatically set by scripts)
NODE_ENV=development

For production builds, create .env.production:

BASE_REST_API=https://your-production-api.com
NODE_ENV=production

Step 4: Set Up OAuth Credentials (Backend)

You need a backend server that handles OAuth. If you don't have one, a reference implementation built with NestJS is available: https://github.com/trae-op/nestjs-boilerplate β€” it includes ready-to-use OAuth endpoints, environment examples, and JWT/session handling.

  1. Configure redirect URIs (example for local dev):

    http://localhost:3000/api/auth/google/callback
    http://localhost:3000/api/auth/github/callback
    
  2. Environment variables (examples used by the reference backend):

    GOOGLE_CLIENT_ID=...
    GOOGLE_CLIENT_SECRET=...
    GITHUB_CLIENT_ID=...
    GITHUB_CLIENT_SECRET=...
    JWT_SECRET=...
    
  3. Implement endpoints (or use the reference):

    • GET /api/auth/google - Redirect to Google OAuth
    • GET /api/auth/github - Redirect to GitHub OAuth
    • GET /api/auth/verify - Return JWT token after successful auth (redirect target after provider auth)
    • GET /api/user/:id - Fetch user by ID (requires Bearer token)

You need a backend server that handles OAuth. If you don't have one, a implementation built with NestJS is available: https://github.com/trae-op/nestjs-boilerplate β€” it includes ready-to-use OAuth endpoints that described in this README.md, environment examples, and JWT/session handling.

Step 5: Run Development Mode

# Start both React dev server and Electron
npm run dev

# Or run separately:
npm run dev:react     # Start Vite dev server (port 5173)
npm run dev:electron  # Start Electron app

πŸ› οΈ Available Scripts

Development

npm run dev              # Run React + Electron in parallel
npm run dev:react        # Start Vite dev server only
npm run dev:electron     # Start Electron only

Building

npm run build            # Build React app (production)
npm run transpile:electron  # Transpile TypeScript (main process)
npm run build:mac        # Build macOS .dmg
npm run build:win        # Build Windows .exe (NSIS)
npm run build:linux      # Build Linux AppImage

Testing

npm run test:unit:renderer  # Run renderer process tests
npm run test:unit:main      # Run main process tests

Code Quality

npm run lint             # Run ESLint
npm run format           # Format code with Prettier

Prettier (Formatting) βœ…

  • Version: prettier (^3.7.4) β€” configured in package.json.
  • Config file: .prettierrc at repo root. Key rules include semi: true, trailingComma: 'all', singleQuote: false, printWidth: 80, tabWidth: 2.
  • Import sorting: Uses @trivago/prettier-plugin-sort-imports with an importOrder array, importOrderSeparation: true, and importOrderSortSpecifiers: true so imports are deterministic and grouped consistently.
  • Format script: npm run format runs prettier --write "src/**/*.{ts,tsx,cts,css,json}".
  • Tip: Enable editor integration (Prettier extension / format on save) and run npm run format before commits to keep code style consistent.

ESLint (Linting) βœ…

  • Config file: eslint.config.js at repo root. It uses typescript-eslint wrapper and @eslint/js recommended rules.
  • Scope & ignore: Lints **/*.{ts,tsx} and ignores dist (see ignores in config).
  • Plugins & rules: Includes eslint-plugin-react-hooks and eslint-plugin-react-refresh. Notable rules: React Hooks recommended rules are enabled and react-refresh/only-export-components is set to warn.
  • Run: npm run lint runs eslint ..
  • Tip: Install the ESLint extension in your editor and enable auto-fix on save for the smoothest workflow.

πŸ—οΈ Building for Production

macOS

npm run build:mac

Output: dist/mac/electron-modular-boilerplate.app and dist/electron-modular-boilerplate-{version}.dmg

Windows

npm run build:win

Output: dist/electron-modular-boilerplate-setup-{version}.exe

Linux

npm run build:linux

Output: dist/reminder-{version}.AppImage

CI/CD with GitHub Actions

The project includes a GitHub Actions workflow that:

  1. Runs on push to main branch
  2. Runs unit tests for both renderer and main processes
  3. Builds for macOS and Windows
  4. Publishes releases to GitHub Releases (if version changed)

Workflow file: .github/workflows/build.yml

To enable auto-publishing:

  1. Create a GitHub Personal Access Token (PAT). For private repositories you need the repo scope; for public-only publishing public_repo may be sufficient.
  2. In your GitHub repository go to Settings β†’ Secrets and variables β†’ Actions and add the token as a secret (common name: GH_TOKEN). Note: GitHub Actions also provides an automatically-generated GITHUB_TOKEN for workflows, but for publishing from private repositories or when workflows need elevated permissions, you should use a PAT stored as a secret.
  3. If your production build or publish scripts need access to the token at runtime, also add GH_TOKEN to your .env.production (do not commit this file). Keep tokens secret and never hardcode them in your repository.

πŸ“ Project Structure Highlights

Reusable UI Components

The project includes 15+ production-ready React components:

  • Button - Primary/secondary/tertiary variants
  • IconButton - Icon-only buttons with tooltips
  • Avatar - User avatar with fallback initials
  • AvatarButton - Avatar with click functionality
  • Popover - Dropdown menus and popovers
  • TextField - Form inputs with validation
  • Select - Custom select dropdowns
  • Checkbox / RadioGroup - Form controls
  • List - Virtualized lists for performance
  • LazyRender - Composite for virtualized rendering (uses react-window + react-virtualized-auto-sizer). Efficiently renders only visible items in a long collection (e.g., 1,000 options may render ~10 visible rows), improving responsiveness for slow renders. Reused by Autocomplete/AutocompleteMultiple to virtualize option lists inside popovers.
  • LoadingSpinner - Loading states
  • Card - Content containers
  • Autocomplete - Search with suggestions (supports multiple selection and uses LazyRender for large option sets)
  • Popup - Modal dialogs

All components are fully typed, tested, and follow Tailwind CSS patterns.

Custom Hooks

  • useClosePreloadWindow - Close splash screen after app loads
  • useDayjs - Localized date formatting
  • useControl - Auth control (login/logout)
  • useSelectors - Subscribe to specific context state slices
  • useDispatch - Get state setter functions

Context Patterns

All contexts use the Subscription Pattern with useSyncExternalStore:

// Avoid unnecessary re-renders
const isAuthenticated = useAuthIsAuthenticatedSelector(); // Only re-renders when auth status changes
const setAuth = useSetAuthIsAuthenticatedDispatch(); // Never re-renders

🎨 Styling

Tailwind CSS Configuration

  • Dark mode support via class strategy
  • Custom color palette with CSS variables
  • Responsive design utilities
  • Custom plugins for animations

Theme Switching

Light/dark mode toggle is built-in:

// src/renderer/composites/LightDarkMode/
const { isDarkMode, toggleTheme } = useLightDarkMode();

πŸ”’ Security

  • Context Isolation enabled in preload script
  • Sandbox enabled for OAuth windows
  • CSP headers in production builds
  • Secure token storage with electron-store
  • No node integration in renderer
  • IPC validation with TypeScript