Electron Forge is a tool that helps developers package and distribute Electron applications. It provides a unified interface for common tasks such as creating a new project, building the application for different platforms, and publishing it. Electron Forge comes with multiple templates and plugins, which can be used to customize the development process according to the requirements.
Vite is a modern build tool that focuses on speed and simplicity. It uses native ES modules in the browser during development, which eliminates the need for bundling upfront. This results in extremely fast cold starts and hot module replacement (HMR). Vite also provides a plugin system that allows developers to extend its functionality.
TypeScript is a superset of JavaScript that adds static typing to the language. It helps catch errors early in the development process by allowing developers to define types for variables, functions, and objects. TypeScript code is transpiled to plain JavaScript, which can be run in any JavaScript environment.
First, make sure you have Node.js and npm (Node Package Manager) installed on your system. Then, create a new Electron Forge project using the following command:
npm init electron-forge@latest my - electron - vite - ts - app --template=webpack
cd my - electron - vite - ts - app
Next, install Vite and TypeScript:
npm install vite typescript @types/node --save - dev
Create a vite.config.ts
file in the root of your project. Here is a basic configuration for an Electron application:
import { defineConfig } from 'vite';
export default defineConfig({
root: 'src/renderer',
build: {
outDir: '../dist/renderer',
emptyOutDir: true
}
});
For TypeScript, create a tsconfig.json
file:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.jsx"]
}
In the src/main
directory, create a main.ts
file for the main process:
import { app, BrowserWindow } from 'electron';
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
win.loadFile('dist/renderer/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();
});
In the src/renderer
directory, create an index.tsx
file:
import React from 'react';
import ReactDOM from 'react - dom/client';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(<h1>Hello, Electron with Vite and TypeScript!</h1>);
A good directory structure can make your project more organized and easier to maintain. Here is a suggested structure:
my - electron - vite - ts - app/
├── src/
│ ├── main/
│ │ └── main.ts
│ ├── renderer/
│ │ ├── index.tsx
│ │ └── index.html
├── dist/
│ ├── renderer/
│ └── main/
├── vite.config.ts
├── tsconfig.json
└── package.json
In Electron, the main process is responsible for creating and managing browser windows, while the renderer process runs the web pages inside those windows. Use the ipcMain
and ipcRenderer
modules to communicate between the main and renderer processes.
// 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/index.tsx
import { ipcRenderer } from 'electron';
ipcRenderer.send('message - from - renderer', 'Hello from renderer');
ipcRenderer.on('message - from - main', (event, arg) => {
console.log(arg);
});
In both the main and renderer processes, implement proper error handling. For example, in the main process, you can handle uncaught exceptions:
// main.ts
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
});
In the renderer process, use try - catch blocks around asynchronous operations:
// renderer/index.tsx
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
Combining Electron Forge, Vite, and TypeScript provides a powerful and efficient way to build cross - platform desktop applications. Electron Forge simplifies the packaging and distribution process, Vite offers a fast development experience, and TypeScript adds static typing for better code quality. By following the usage methods, common practices, and best practices outlined in this blog post, you can create robust and maintainable desktop applications.