# Vite Entry Points for Electron Windows in escrcpy: Multi-Page Build Guide

> Discover how escrcpy leverages Vite entry points for Electron windows, building separate interfaces for main app, control, explorer, and terminal. Learn multi-page build strategies.

- Repository: [viarotel-org/escrcpy](https://github.com/viarotel-org/escrcpy)
- Tags: how-to-guide
- Published: 2026-09-10

---

**escrcpy uses a single Vite configuration in [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js) to define multiple HTML entry points for each Electron window, enabling separate builds for the main application, control, explorer, and terminal interfaces.**

The escrcpy project leverages Vite’s multi-page build capabilities to manage a complex Electron application architecture with multiple renderer windows. By centralizing the build configuration in [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js), the repository maintains consistent tooling across all window types while allowing each interface to be built, hot-reloaded, and packaged independently. This approach creates a **multi-page build** through the `rolldownOptions.input` configuration, generating distinct HTML outputs for every window the main process can load.

## Understanding the Multi-Page Vite Configuration

The configuration file at [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js) serves as the central build orchestrator for the entire desktop application. It defines all entry points—both for renderer processes and Electron’s main process—within a single configuration object that shares common settings via `mergeCommon`. When Vite runs, it processes each entry point into separate bundles, outputting them to the `dist-electron/` directory for the production build.

## Renderer Process Entry Points

escrcpy defines four distinct renderer entry points, each corresponding to a specific Electron window type. These are configured in the `build.rolldownOptions.input` section of the Vite config.

### Main Application Window

The default application window loads from the project root HTML file:

- **Entry**: [`index.html`](https://github.com/viarotel-org/escrcpy/blob/main/index.html) (referenced at [`vite.config.js:65`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L65))
- **Output**: [`dist-electron/index.html`](https://github.com/viarotel-org/escrcpy/blob/main/dist-electron/index.html)

### Control Window (Device Control UI)

This window provides the interface for controlling connected Android devices:

- **Entry**: [`pages/control/index.html`](https://github.com/viarotel-org/escrcpy/blob/main/pages/control/index.html) (referenced at [`vite.config.js:66`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L66))
- **Output**: [`dist-electron/control.html`](https://github.com/viarotel-org/escrcpy/blob/main/dist-electron/control.html)

### Explorer Window (File Explorer UI)

The file explorer interface allows browsing device filesystems:

- **Entry**: [`pages/explorer/index.html`](https://github.com/viarotel-org/escrcpy/blob/main/pages/explorer/index.html) (referenced at [`vite.config.js:67`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L67))
- **Output**: [`dist-electron/explorer.html`](https://github.com/viarotel-org/escrcpy/blob/main/dist-electron/explorer.html)

### Terminal Window (Embedded Shell)

This provides an embedded terminal interface for shell access:

- **Entry**: [`pages/terminal/index.html`](https://github.com/viarotel-org/escrcpy/blob/main/pages/terminal/index.html) (referenced at [`vite.config.js:68`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L68))
- **Output**: [`dist-electron/terminal.html`](https://github.com/viarotel-org/escrcpy/blob/main/dist-electron/terminal.html)

## Main Process and Preload Script Entries

In addition to renderer entries, the same Vite configuration builds the Electron main process and preload script:

| Process | Vite Entry | Configuration Location |
|---------|------------|------------------------|
| **Main** (Electron’s entry point) | [`electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/electron/main.js) | [`vite.config.js:20`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L20) |
| **Preload** (exposes safe APIs to renderers) | [`electron/preload.js`](https://github.com/viarotel-org/escrcpy/blob/main/electron/preload.js) | [`vite.config.js:29`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js#L29) |

The preload script entry is particularly critical for security, as it builds the script that safely exposes Node.js and Electron APIs to the renderer processes through context isolation.

## How the Main Process Loads Vite-Built Windows

When the application starts, the main process loads the appropriate HTML file for each window using the `file://` protocol. The `windowManager.createWindow()` function in [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js) references the built HTML outputs in `dist-electron/`:

```javascript
// desktop/electron/main.js (simplified)
await windowManager.createWindow({
  name: 'control',
  url: `file://${path.resolve('dist-electron/control.html')}`,
});

```

This architecture decouples the window definitions from the build process—the main process simply loads whatever HTML bundles Vite produces in the output directory.

## Development and Production Workflows

### Running the Development Server

During development, Vite serves the renderer bundles on port 1535, enabling hot module replacement for all window types:

```bash
pnpm dev          # launches Vite dev server (port 1535) and Electron

```

The dev server simultaneously watches the main process and preload script entries, restarting Electron when those change.

### Building for Production

The production build generates separate HTML files for each entry point under `dist-electron/`:

```bash
pnpm build        # => dist-electron/

# ├─ index.html          (main window)

# ├─ control.html        (control window)

# ├─ explorer.html       (explorer window)

# └─ terminal.html       (terminal window)

```

Each HTML file includes the properly bundled JavaScript and CSS assets for that specific window.

## Extending the Configuration: Adding New Windows

To add a new window type to escrcpy, you must define both the Vite entry point and the main process window creation logic.

**Step 1**: Add the entry to [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js):

```javascript
build: {
  rolldownOptions: {
    input: {
      ...,
      copilot: resolve('pages/copilot/index.html'),   // new window entry
    },
  },
}

```

**Step 2**: Open the window from the main process in [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js):

```javascript
await windowManager.createWindow({
  name: 'copilot',
  url: `file://${path.resolve('dist-electron/copilot.html')}`,
});

```

This pattern maintains the modular architecture while extending the application's window ecosystem.

## Summary

- escrcpy configures **four renderer entry points** (main, control, explorer, terminal) and **two Electron process entries** (main, preload) in [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js)
- The multi-page build outputs separate HTML files to `dist-electron/` for each window type, renamed from their original paths
- The main process loads windows via the `file://` protocol, pointing to the built HTML entries in the distribution directory
- New windows require adding entries to the `rolldownOptions.input` configuration and corresponding `windowManager.createWindow()` calls

## Frequently Asked Questions

### How many renderer entry points does escrcpy define in its Vite configuration?

escrcpy defines four renderer entry points in [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js): the main window at [`index.html`](https://github.com/viarotel-org/escrcpy/blob/main/index.html) (line 65), plus the control (line 66), explorer (line 67), and terminal (line 68) windows under `pages/`. Each entry corresponds to a distinct Electron window type with its own HTML output.

### Where does escrcpy configure the Electron main process entry point?

The main process entry point is configured at line 20 of [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js), pointing to [`electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/electron/main.js). This entry builds the Node.js process that manages window creation, lifecycle events, and native system integration.

### What is the purpose of the preload script entry in escrcpy's Vite config?

The preload script entry at line 29 of [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js) builds [`electron/preload.js`](https://github.com/viarotel-org/escrcpy/blob/main/electron/preload.js), which securely exposes selected Node.js and Electron APIs to renderer processes through context isolation. This follows Electron security best practices by preventing direct access to Node.js from renderer contexts.

### How do I add a custom window to escrcpy without breaking the existing build?

Add a new entry to `build.rolldownOptions.input` in [`desktop/vite.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/vite.config.js) pointing to your new HTML file (e.g., [`pages/custom/index.html`](https://github.com/viarotel-org/escrcpy/blob/main/pages/custom/index.html)), then instantiate the window in [`desktop/electron/main.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron/main.js) using `windowManager.createWindow()` with the corresponding `dist-electron/` HTML output path. The existing mergeCommon configuration ensures your new entry inherits all shared aliases and plugins automatically.