Building Desktop Applications with Electron, Vite, React, and TypeScript

In the world of modern desktop application development, combining powerful technologies can lead to highly efficient and maintainable projects. Electron, Vite, React, and TypeScript are four such technologies that, when used together, can create a robust environment for building cross - platform desktop applications. Electron is an open - source framework that allows you to build desktop applications using web technologies such as HTML, CSS, and JavaScript. Vite is a build tool that provides a faster and leaner development experience for modern web projects. React is a popular JavaScript library for building user interfaces, and TypeScript is a superset of JavaScript that adds static typing, which helps catch errors early in the development process. This blog post will guide you through the fundamental concepts, usage methods, common practices, and best practices of using Electron, Vite, React, and TypeScript together.

Table of Contents

  1. [Fundamental Concepts](#fundamental - concepts)
  2. [Setting Up the Project](#setting - up - the - project)
  3. [Usage Methods](#usage - methods)
  4. [Common Practices](#common - practices)
  5. [Best Practices](#best - practices)
  6. Conclusion
  7. References

Fundamental Concepts

Electron

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

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

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

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.

Setting Up the Project

Step 1: Initialize a new Vite project with React and TypeScript

npm init vite@latest my - electron - app -- --template react - ts
cd my - electron - app

Step 2: Install Electron

npm install electron --save - dev

Step 3: Configure Vite for Electron

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',
    },
});

Step 4: Create the main Electron file

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();
});

Step 5: Add scripts to package.json

{
    "scripts": {
        "dev": "vite",
        "build": "tsc && vite build",
        "start": "electron. "
    }
}

Usage Methods

Developing the React UI

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;

Communicating between the main and renderer processes

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;

Common Practices

Error handling

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);
    }
}

Using preload scripts

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);
});

Best Practices

Security

  • Limit the Node.js APIs exposed to the renderer process using preload scripts.
  • Validate and sanitize all user input to prevent injection attacks.
  • Keep Electron and all dependencies up - to - date to patch security vulnerabilities.

Performance

  • Use React’s memoization techniques (e.g., React.memo) to prevent unnecessary re - renders.
  • Optimize the build process by using Vite’s build options to minify and tree - shake your code.

Code organization

  • Follow a modular approach by splitting your code into smaller, reusable components.
  • Use a consistent naming convention for files, components, and variables.

Conclusion

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.

References