A modern Electron application template with React, Vite, TypeScript, and TailwindCSS. This project provides a solid foundation for developing cross-platform desktop applications.
Stack
🔹 Electron - Cross-platform desktop application framework.
🔹 React - The library for web and native user interfaces.
🔹 TypeScript - Type-safe JavaScript.
🔹 Shadcn UI - Beautiful and accessible component library.
🔹 TailwindCSS - Utility-first CSS framework.
🔹 Electron Vite - Lightning-fast build tool based on Vite for fastest hot-reload.
🔹 Electron Builder - Configured for packaging applications.
In-Built Features
| Feature | Description |
|---|---|
| Conveyor | Type-safe inter-process communication with Zod validation |
| Custom Titlebar & Menus | Style the window titlebar and menus as you want |
| Clean Project Structure | Separation of main and renderer processes |
| Resources Protocol | Access local file resources via res:// protocol |
| Import Path Aliases | Keep your imports organized and clean |
| Theme Switcher | Built-in theme switching for dark and light mode |
| Error Boundary | Built-in React error boundary with detailed error reporting |
| Welcome Kit | Interactive showcase with Framer Motion animations |
| Code Formatting | Prettier and ESLint pre-configured for code quality |
| Hot Reload | Lightning-fast development with Vite's HMR |
| VS Code Debugging | Pre-configured launch configurations for debugging main and renderer processes |
Installation
Clone the repository:
# Clone the repository git clone https://github.com/guasam/electron-react-app # Change directory cd electron-react-app # Install dependencies (use any package manager: npm, yarn, pnpm, bun) npm install
Development
Start the development server:
This will start Electron with hot-reload enabled so you can see changes in real time.
Conveyor - Inter-Process Communication
Conveyor is a type-safe IPC system that enables secure communication between your React frontend and Electron's main process. It uses Zod schemas for runtime validation and provides full TypeScript support.
🔹 Type-safe - Full TypeScript support with compile-time and runtime validation
🔹 Secure - Validates all data using Zod schemas
🔹 Modular - Clean API structure with organized handlers
🔹 Simple - Easy-to-use React hooks and global APIs
Quick Start
Use the useConveyor hook in your React components:
import { useConveyor } from '@/app/hooks/use-conveyor' function MyComponent() { const { version } = useConveyor('app') const { windowMinimize } = useConveyor('window') const handleGetVersion = async () => { console.log('App version:', await version()) console.log('App version:', await window.conveyor.app.version()) // OR } return ( <div> <button onClick={handleGetVersion}>Get Version</button> <button onClick={windowMinimize}>Minimize Window</button> </div> ) }
Available APIs
Conveyor provides two ways to access IPC methods:
// Method 1: React Hook (Recommended) const { version } = useConveyor('app') await version() // Method 2: React Hook Global Conveyor const conveyor = useConveyor() await conveyor.app.version() // Method 3: Global Window Object await window.conveyor.app.version()
Built-in APIs
| API | Description | Example |
|---|---|---|
app |
App specfiic operations | conveyor.app.version() |
window |
Window specific operations | conveyor.window.windowMinimize() |
Creating Custom APIs
Follow these 4 simple steps to add your own IPC methods:
Step 1: Define Schema
Create a schema in lib/conveyor/schemas/app-schema.ts:
import { z } from 'zod' export const appIpcSchema = { // Simple method with no parameters 'get-app-info': { args: z.tuple([]), return: z.object({ name: z.string(), version: z.string(), platform: z.string(), }), }, // Method with parameters 'save-user-preference': { args: z.tuple([ z.object({ key: z.string(), value: z.string(), }), ]), return: z.boolean(), }, } as const
Step 2: Add API Method
Update lib/conveyor/api/app-api.ts:
export class AppApi extends ConveyorApi { getAppInfo = () => this.invoke('get-app-info') saveUserPreference = (key: string, value: string) => this.invoke('save-user-preference', { key, value }) }
Step 3: Implement Handler
Add handler in lib/conveyor/handlers/app-handler.ts:
import { handle } from '@/lib/main/shared' import { app } from 'electron' export const registerAppHandlers = () => { handle('get-app-info', () => ({ name: app.getName(), version: app.getVersion(), platform: process.platform, })) handle('save-user-preference', async ({ key, value }) => { // Save to file, database, etc. console.log(`Saving ${key}: ${value}`) return true }) }
Step 4: Register Handler
In lib/main/app.ts:
import { registerAppHandlers } from '@/lib/conveyor/handlers/app-handler' // During app initialization registerAppHandlers()
Usage in Components
function SettingsComponent() { const conveyor = useConveyor() const [appInfo, setAppInfo] = useState(null) useEffect(() => { // Get app information conveyor.app.getAppInfo().then(setAppInfo) }, []) const saveTheme = (theme: string) => { conveyor.app.saveUserPreference('theme', theme) } return ( <div> <h2>App Info</h2> {appInfo && ( <p> {appInfo.name} v{appInfo.version} on {appInfo.platform} </p> )} <button onClick={() => saveTheme('dark')}>Set Dark Theme</button> </div> ) }
Error Handling
const handleApiCall = async () => { try { const result = await conveyor.app.getAppInfo() console.log('Success:', result) } catch (error) { console.error('API call failed:', error) // Handle validation errors, network issues, etc. } }
Type Safety Benefits
// ✅ TypeScript enforces correct types const info = await conveyor.app.getAppInfo() // Returns { name: string, version: string, platform: string } // ❌ TypeScript error - wrong parameter type const result = await conveyor.app.saveUserPreference(123, 'value') // Error: Expected string, got number // ✅ Runtime validation ensures data integrity const valid = await conveyor.app.saveUserPreference('theme', 'dark') // Validates at runtime
📖 For advanced usage and detailed documentation, see Conveyor README
Custom Window Components
This template includes a custom window implementation with:
- Custom titlebar with app icon
- Window control buttons (minimize, maximize, close)
- Menu system with keyboard shortcuts
- Dark/light mode toggle
- Cross-platform support for Windows and macOS
Titlebar Menu Toggle
The titlebar menu can be toggled using:
- Windows: Press the
Altkey - macOS: Press the
Option (⌥)key
When you press the toggle key:
- If the menu is hidden, it becomes visible
- If the menu is already visible, it gets hidden
- The menu only toggles if menu items are available
Customizing Menu Items
To add, remove or modify menu items, update the following file:
app/components/window/menus.ts
Tailwind CSS
The project supports Tailwind for styling:
// Example component with Tailwind classes const Button = () => ( <button className="px-4 py-2 text-white rounded-md"> Click me </button> );
Key Directories Explained
app/ - Renderer Process
- React application that runs in the browser window
- Contains all UI components, styles, and client-side logic
- Uses Vite for fast development and building
lib/conveyor/ - Conveyor - Inter-Process Communication
- Type-safe communication between renderer and main processes
- API classes provide clean interfaces for IPC calls
- Handlers implement the actual logic in the main process
- Schemas define data contracts with Zod validation
lib/main/ - Main Process
- Electron main process code
- Handles window creation, app lifecycle, and system integration
- Registers IPC handlers and manages app state
lib/preload/ - Preload Scripts
- Security bridge between renderer and main processes
- Exposes safe APIs to the renderer process
- Implements context isolation for security
Development Workflow
- UI Development: Work in
app/directory with React components - IPC Communication: Define schemas, add API methods, implement handlers
- Window Features: Customize window behavior in
app/components/window/ - Prettier Formatting: Use
npm run formatto format the code. - ESLint: Use
npm run lintto lint the code.
Path Aliases
The project uses TypeScript path aliases for clean imports:
// Instead of relative paths like: import { Button } from '../../../components/ui/button' // Use clean aliases: import { Button } from '@/app/components/ui/button' import { conveyor } from '@/lib/conveyor/api'
Configured aliases by default, customise as you want:
@/→app/(application code - renderer process)@/lib/→lib/(shared library code containing conveyor, main, preload, etc.)@/resources/→resources/(build resources for the application)
Building for Production
Build the application for your platform:
# For Windows npm run build:win # For macOS npm run build:mac # For Linux npm run build:linux # Unpacked for all platforms npm run build:unpack
Distribution files will be located in the dist directory.
