DEV Community

Charan Gutti
Charan Gutti

Posted on

Vite Electron in simple terms and its setup

Introduction

Electron is a great framework to build desktop applications. Using vite electron which is a new framework for electron helps in building the applications even faster and also comes with all the benefits that vite has.

Sometimes it can be confusing working with vite electron but lets break it down for easier understanding.

The Complete Setup

I am using react as my main renderer framework to create the front-end.

First, let's lay out all the code for a minimal, working Vite + Electron application. This will be our reference point.

Project Structure

Imagine we've created a project. The file structure would look like this:

my-electron-app/
├── electron/
│   ├── main.js       # Main process entry point
│   └── preload.js    # Preload script (the bridge)
├── src/
│   ├── App.jsx       # A simple React component
│   └── main.jsx      # Renderer process entry point
├── index.html
├── package.json
└── vite.config.js
Enter fullscreen mode Exit fullscreen mode

package.json

This file defines our project, its dependencies, and the scripts to run and build it.
JSON

{
  "name": "my-electron-app",
  "version": "0.1.0",
  "main": "electron/main.js", // <-- Important: Points to Electron's entry file
  "scripts": {
    "dev": "vite",
    "build": "vite build && electron-builder"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.2.1",
    "electron": "^28.2.1",
    "electron-builder": "^24.9.1",
    "vite": "^5.0.12",
    "vite-plugin-electron-renderer": "^0.14.5"
  },
  "build": { // <-- Configuration for electron-builder
    "appId": "com.my-app.id",
    "productName": "My Awesome App",
    "files": [
      "dist/**/*",
      "electron/**/*"
    ],
    "directories": {
      "buildResources": "assets",
      "output": "release"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

vite.config.js

This configures Vite to work nicely with Electron.
JavaScript

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import electron from 'vite-plugin-electron-renderer';

export default defineConfig({
  plugins: [
    react(),
    electron(), // <-- The key plugin for Electron
  ],
});
Enter fullscreen mode Exit fullscreen mode

**
electron/main.js (The Main Process)**

This script is the heart of your Electron app. It runs in a Node.js environment and is responsible for creating windows and handling system events.
JavaScript

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
  app.quit();
}

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'), // <-- Link to the preload script
      contextIsolation: true, // <-- Security feature
      nodeIntegration: false, // <-- Security feature
    },
  });

  // Load the app.
  if (process.env.VITE_DEV_SERVER_URL) {
    // In development, load from the Vite dev server.
    mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
    // Open the DevTools.
    mainWindow.webContents.openDevTools();
  } else {
    // In production, load the built index.html file.
    mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
  }
};

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
Enter fullscreen mode Exit fullscreen mode

electron/preload.js (The Bridge)

This special script acts as a secure bridge between your web-based frontend (Renderer) and the Node.js backend (Main).
JavaScript

const { contextBridge, ipcRenderer } = require('electron');

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('api', {
  send: (channel, data) => {
    // whitelist channels
    let validChannels = ['toMain'];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  },
  receive: (channel, func) => {
    let validChannels = ['fromMain'];
    if (validChannels.includes(channel)) {
      // Deliberately strip event as it includes `sender`
      ipcRenderer.on(channel, (event, ...args) => func(...args));
    }
  }
});

Enter fullscreen mode Exit fullscreen mode

src/main.jsx and src/App.jsx (The Renderer Process)

This is your standard Vite/React frontend code. It runs in a browser-like environment.

src/main.jsx
JavaScript

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

src/App.jsx
JavaScript

import React from 'react';

function App() {
  const handleClick = () => {
    // Use the exposed 'api' from the preload script
    window.api.send('toMain', 'Hello from the Renderer Process!');
  };

  React.useEffect(() => {
    window.api.receive('fromMain', (data) => {
      console.log(`Received from main: ${data}`);
    });
  }, []);

  return (
    <div>
      <h1>Hello from Vite + React + Electron!</h1>
      <button onClick={handleClick}>Send Message to Main</button>
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Lets go to Q/A for better understanding.

How would you describe the fundamental difference between the "Main Process"? What can one do that the other cannot?

Answer:

  • The Main Process is the stage manager🎭. it sets up the stage(the window) and manages the backstage opertaions.
  • The Renderer Process is the actor 🧑‍🎤. It performs on the stage, displaying the content and interacting with the user.

Top comments (0)