Electron React App
February 12, 2026 · View on GitHub
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:
npm run dev
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.