Building Desktop Applications with Vue and Electron

Setting Up a Vite, Vue 3, and JavaScript Project

Ensure all JSON configuration files do not contain comments, as they may cause runtime issues even if no immediate errors are shown.

1. Creating the Desktop Application

1.1 Project Initialization

Run the following command to create a new project:

npm create vite@latest

When prompted, select the Vue 3 framwork with either JavaScript or TypeScript.

1.2 Installing Dependencies and Starting the Project

Navigate into the project directory and install the dependencies, then run the development server.

npm install
npm run dev

1.3 Integrating Electron

Install Electron as a dependency. Using a mirror is recommended for faster installation.

npm install electron

To configure an npm mirror, create a .npmrc file in your project root and add the following line:

ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"

1.4 Configuring Application Files

Vite Configuration (vite.config.js)

Set the base property to ensure static assets are correctly referenced when the application is built.

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  base: './', // Use relative paths for asset resolution
  plugins: [vue()]
});
Electron Main Process (main.js)

Create a new main.js file in the project root. This is the entry point for the Electron application.

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

function initializeWindow() {
  const primaryWindow = new BrowserWindow({
    width: 1024,
    height: 768,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  primaryWindow.loadFile('dist/index.html');
  // primaryWindow.webContents.openDevTools(); // Enable for debugging
}

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

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

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
Preload Script (preload.js)

Create a preload.js file to safely expose Node.js APIs to the renderer process.

window.addEventListener('DOMContentLoaded', () => {
  const updateElement = (id, content) => {
    const targetElement = document.getElementById(id);
    if (targetElement) targetElement.textContent = content;
  };

  ['chrome', 'node', 'electron'].forEach(lib => {
    updateElement(`${lib}-version`, process.versions[lib]);
  });
});
Package Configuration (package.json)

Update your package.json to include the main entry point and the Electron start script. Remove the "type": "module" line if it exists.

{
  "name": "my-electron-app",
  "private": true,
  "version": "0.0.0",
  "main": "main.js",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "electron:start": "electron ."
  },
  "dependencies": {
    "electron": "^27.0.0",
    "vue": "^3.3.4"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.3",
    "vite": "^4.4.5"
  }
}

1.5 Building the Vue Application

Run the build command to generate the dist folder.

npm run build

1.6 Launching the Electron Application

Start the built application.

npm run electron:start

2. Implementing Hot Reload for Development

2.1 Modify the Main Process for Development

Update main.js to load the application from the Vite development server URL instead of a static file.

// ... previous app setup code
function initializeWindow() {
  // ... window creation settings
  // Replace loadFile with loadURL for development
  primaryWindow.loadURL("http://localhost:5173");
}
// ... rest of the code

2.2 Running Both Servers Concurrently

Install concurrently and wait-on to run the Vite dev server and Electron simultaneously.

npm install -D concurrently wait-on

Update the scripts in package.json:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "electron:dev": "wait-on tcp:5173 && electron .",
    "electron:start": "concurrently -k \"npm run dev\" \"npm run electron:dev\""
  }
}

Run the application in development mode with hot reload:

npm run electron:start

3. Building the Application for Production

The key is to make the main process load the correct source based on the environment variable NODE_ENV.

3.1 Organizing Files

Create an electron folder in the project root and move main.js and preload.js into it.

3.2 Configuring the Main Process for Different Environments

Update electron/main.js to conditionally load resources.

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

const appMode = process.env.NODE_ENV;

function initializeWindow() {
  const primaryWindow = new BrowserWindow({
    width: 1024,
    height: 768,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  });

  const appUrl = appMode === 'development'
    ? 'http://localhost:5173'
    : `file://${path.join(__dirname, '../dist/index.html')}`;
  primaryWindow.loadURL(appUrl);

  if (appMode === 'development') {
    primaryWindow.webContents.openDevTools();
  }
}
// ... rest of the app lifecycle code

3.3 Updating Package Configuration for Building

Modify package.json to specify the new main entry point and add build configuration for electron-builder. Install the necessary build tools.

npm install -D cross-env electron-builder

Update package.json:

{
  "name": "my-electron-app",
  "private": true,
  "version": "0.0.0",
  "main": "electron/main.js",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "electron:dev": "wait-on tcp:5173 && cross-env NODE_ENV=development electron .",
    "electron:start": "concurrently -k \"npm run dev\" \"npm run electron:dev\"",
    "electron:build": "vite build && electron-builder"
  },
  "build": {
    "appId": "com.example.myapp",
    "productName": "MyElectronApp",
    "copyright": "Copyright © 2023",
    "mac": {
      "category": "public.app-category.developer-tools"
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    },
    "files": [
      "dist/**/*",
      "electron/**/*"
    ],
    "directories": {
      "buildResources": "assets",
      "output": "dist_electron"
    }
  },
  "dependencies": {},
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.3",
    "concurrently": "^8.2.1",
    "cross-env": "^7.0.3",
    "electron": "^27.0.0",
    "electron-builder": "^24.9.1",
    "vite": "^4.4.5",
    "vue": "^3.3.4",
    "wait-on": "^7.0.1"
  }
}

3.4 Executing the Production Build

Run the build command to generate the distributable application.

npm run electron:build

This command first builds the Vue application and then packages it with Electron, outputting the final executable to the dist_electron directory.

Alternative: Using Pre-configured Templates

Several community projects offer pre-configured setups for Vue, Electron, and TypeScript, which can simplify the initial process.

Using the create electron-vite Command

This method scaffolds a project with Vite, Vue 3, and TypeScript already configured.

npm create electron-vite

Follow the prompts to name your project and select the Vue template.

Using a Scaffolded Project Template

Another approach is to use a specific generator that supports multiple frontend frameworks.

yarn create electron-vite my-project

Select the Vue template when prompted. Development and build commands are typically yarn run dev and yarn run build.

Common Build Issues and Solutions

  • TypeScript Compilation Errors: If vue-tsc fails during build, you may need to remove it from the build script in package.json.
  • Slow Binary Downloadss: Add mirror URLs to your .npmrc file to speed up downloads of Electron builder binaries.
  • Symbolic Link Permission Errors: On Windows, running the build command in an elevated (Administrator) PowerShell session can resolve permissions issues.

Tags: vue electron Desktop Application Vite javascript

Posted on Mon, 29 Jun 2026 17:13:30 +0000 by ashu.khetan