Electron combines the Chromium rendering engine and the Node.js runtime. It has two main types of processes: the main process and the renderer process. The main process is responsible for creating and managing browser windows, handling system events, and interacting with the operating system. The renderer process runs the web pages inside the browser windows and is similar to a traditional web application.
Vite uses native ES modules in the browser during development, which allows for instant hot module replacement (HMR) and fast startup times. It also provides a simple and flexible configuration system and can optimize your code for production.
React is based on the concept of components. Components are reusable pieces of code that can manage their own state and render user interfaces. React uses a virtual DOM to efficiently update the actual DOM when the state of a component changes.
TypeScript adds static typing to JavaScript. By defining types for variables, functions, and objects, you can catch type - related errors at compile - time rather than at runtime. This makes the code more predictable and easier to maintain.
npm init vite@latest my - electron - app -- --template react - ts
cd my - electron - app
npm install electron --save - dev
Create a vite.config.ts
file if it doesn’t exist and configure it for Electron. Here is a basic example:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin - react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist',
minify: 'esbuild',
},
});
Create a main.ts
file in the root directory. This file will be the entry point for the main Electron process.
import { app, BrowserWindow } from 'electron';
import path from 'path';
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
},
});
win.loadFile('dist/index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window - all - closed', function () {
if (process.platform!== 'darwin') app.quit();
});
package.json
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"start": "electron. "
}
}
In the src
directory, you can create React components as you normally would. For example, create a simple App.tsx
component:
import React from'react';
function App() {
return (
<div>
<h1>Hello, Electron + Vite + React + TypeScript!</h1>
</div>
);
}
export default App;
Electron provides several ways to communicate between the main and renderer processes. One common way is to use the ipcMain
and ipcRenderer
modules.
Main process (main.ts
):
import { app, BrowserWindow, ipcMain } from 'electron';
ipcMain.on('message - from - renderer', (event, arg) => {
console.log(arg);
event.sender.send('message - from - main', 'Message received in main process');
});
Renderer process (src/App.tsx
):
import React, { useEffect } from'react';
import { ipcRenderer } from 'electron';
function App() {
useEffect(() => {
ipcRenderer.send('message - from - renderer', 'Hello from renderer');
ipcRenderer.on('message - from - main', (event, arg) => {
console.log(arg);
});
return () => {
ipcRenderer.removeAllListeners('message - from - main');
};
}, []);
return (
<div>
<h1>Hello, Electron + Vite + React + TypeScript!</h1>
</div>
);
}
export default App;
In the main process, handle errors when creating browser windows or loading files. For example:
function createWindow() {
try {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
},
});
win.loadFile('dist/index.html');
} catch (error) {
console.error('Error creating window:', error);
}
}
Preload scripts are used to expose specific Node.js APIs to the renderer process in a controlled way. For example, create a preload.js
file:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
sendMessage: (message) => ipcRenderer.send('message - from - renderer', message),
receiveMessage: (callback) => ipcRenderer.on('message - from - main', callback),
});
In the renderer process, you can then use these APIs:
window.electronAPI.sendMessage('Hello from renderer using preload');
window.electronAPI.receiveMessage((event, arg) => {
console.log(arg);
});
React.memo
) to prevent unnecessary re - renders.Combining Electron, Vite, React, and TypeScript provides a powerful and efficient way to build cross - platform desktop applications. By understanding the fundamental concepts, following the usage methods, common practices, and best practices outlined in this blog post, you can create high - quality, maintainable, and secure desktop applications.