I'm building an Electron JS app using Electron Forge and Vite, and I'd like to display a second window in the application, but it isn't displaying properly. When I run npm run start
the second window opens, but it displays the same content as the first window. When I run npm run make
it opens the second window still, but the window is empty.
I realize there are lots of documented ways to do this, but most use web pack, or use Electron Forge without Vite.
I've done a bare bones electron forge app and just added a second window. I generated it with the following.
npm init electron-app@latest electron-forge-2-windows -- --template=vite
I have the following directory structure.
.
├── forge.config.js
├── index.html
├── modalWindow.html
├── package.json
├── src
│ ├── index.css
│ ├── main.js
│ ├── preload.js
│ └── renderer.js
├── vite.main.config.mjs
├── vite.modal_renderer.config.mjs
├── vite.preload.config.mjs
├── vite.renderer.config.mjs
└── yarn.lock
forge.config.js
module.exports = {
packagerConfig: {},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [
{
name: '@electron-forge/plugin-vite',
config: {
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: 'src/main.js',
config: 'vite.main.config.mjs',
},
{
entry: 'src/preload.js',
config: 'vite.preload.config.mjs',
},
],
renderer: [
{
name: 'main_window',
config: 'vite.renderer.config.mjs',
},
{
name: 'modal_window',
config: 'vite.modal_renderer.config.mjs',
},
],
},
},
],
};
src/main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
const modalWindow = new BrowserWindow({
parent: mainWindow,
modal: true,
show: false,
width: 200,
height: 200,
});
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}
if (MODAL_WINDOW_VITE_DEV_SERVER_URL) {
modalWindow.loadURL(MODAL_WINDOW_VITE_DEV_SERVER_URL);
} else {
modalWindow.loadFile(path.join(__dirname, `../renderer/${MODAL_WINDOW_VITE_NAME}/index.html`));
}
modalWindow.show();
};
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
vite.main.config.mjs
import { defineConfig } from 'vite';
// https://vitejs.dev/config
export default defineConfig({});
vite.renderer.config.mjs
import { defineConfig } from 'vite';
// https://vitejs.dev/config
export default defineConfig({});
vite.modal_renderer.config.mjs
import { defineConfig } from 'vite';
import { resolve } from 'path';
// https://vitejs.dev/config
export default defineConfig({
build: {
rollupOptions: {
input: {
modal_window: resolve(__dirname, 'modalWindow.html'),
},
},
},
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
</head>
<body>
<h1>💖 Hello World!</h1>
<p>Welcome to your Electron application.</p>
<script type="module" src="/src/renderer.js"></script>
</body>
</html>
modalWindow.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
</head>
<body>
<h1>Modal Window</h1>
<div>Modal</div>
</body>
</html>
I can see that in my main.js the MODAL_WINDOW_VITE_DEV_SERVER_URL
constant is resolving to http://localhost:5174
which seems right, but I don't understand what ties that URL to the correct html file. I'm guessing what I did in vite.modal_renderer.config.mjs
is supposed to do that, but clearly I did something wrong.
root
the output is the same folder. This means when you build your Electron app, withelectron-forge package
for example, the built code does not end up in your app. – Moradabad