Electron creating and managing windows

In Electron, windows (Window) are the core medium for interaction between the application and the user.

All visible interfaces run onRenderer Processand the creation and control of these windows is handled byMain Processresponsible.


Creating and managing windows

Electron throughBrowserWindowclass to create windows.

In the main process, a new window is defined in the following way:

Example

const { BrowserWindow } = require('electron')

let mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  show: false, // Wait until the page is ready before displaying
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
  },
})

BrowserWindow configuration options

  • width / height: Window width and height.
  • minWidth / minHeight: Minimum size limit.
  • resizable: Whether resizing is allowed.
  • fullscreen / fullscreenable: Whether to go fullscreen.
  • frame: Whether to show the native title bar (often disabled when using a custom title bar).
  • transparent: Transparent window (can achieve effects such as frosted glass, floating, etc.).
  • alwaysOnTop: Whether the window is always on top.

Window size, position, and state
The window state can be controlled via the API:

mainWindow.maximize()   // 最大化
mainWindow.minimize()   // 最小化
mainWindow.restore()    // 还原
mainWindow.setBounds({ x: 100, y: 100, width: 800, height: 600 }) // 调整位置和大小

You can also listen for window changes:

mainWindow.on('resize', () => console.log('窗口尺寸变化'))
mainWindow.on('move', () => console.log('窗口移动'))

Frameless windows and custom title bar
Settingsframe: falseAfterward, you can customize the title bar area and simulate dragging using CSS and JS:

.titlebar {
  -webkit-app-region: drag;
}

Note that non-draggable areas (such as buttons) should be added with:

.titlebar {
  -webkit-app-region: drag;
}

Window event listening

mainWindow.on('close', (e) => {
  e.preventDefault()
  mainWindow.hide()
})

Common events include:

  • ready-to-show: The page has finished loading and can be displayed safely.
  • focus / blur: The window gains or loses focus.
  • closed: The window has been destroyed.

Loading page content

Electron windows can load local HTML files or remote websites.

Load local HTML files

mainWindow.loadFile('index.html')

Load remote URL

mainWindow.loadURL('https://example.com')

Using frontend frameworks
If you use modern frameworks such as Vue, React, or Angular, you can load the built static files into the window:

mainWindow.loadFile(path.join(__dirname, 'dist/index.html'))

Or load a local service in development mode:

mainWindow.loadURL('http://localhost:5173')

Inter-window communication

In multi-window applications, you may need to pass data between windows or control behavior.

Parent-child window relationship
Can be specified when creating a new windowparent:

let child = new BrowserWindow({
  parent: mainWindow,
  modal: true, // 模态窗口
  width: 400,
  height: 300,
})

When the parent window is closed, child windows will also close automatically.

Multi-window data sharing
You can use the main process as an intermediary to pass data between different renderer processes, or use global objects oripcMain / ipcRenderer。

Window message passing
The main process can directly operate on the target window:

child.webContents.send('update-data', someData)

Receive in the child window:

const { ipcRenderer } = require('electron')
ipcRenderer.on('update-data', (event, data) => {
  console.log(data)
})

Interface optimization

Splash Screen
When the main window loads slowly, you can first display a lightweight splash window:

const splash = new BrowserWindow({ width: 400, height: 300, frame: false })
splash.loadFile('splash.html')

mainWindow.once('ready-to-show', () => {
  splash.close()
  mainWindow.show()
})

Window display optimization
Common techniques to prevent white-screen flickering include:

  • Usageshow: falseCreate a window, and after loading is complete,show()。
  • Set the background color on the rendered page.
  • Preload resources (icons, fonts, styles).

Techniques to prevent flickering

  • Enable GPU acceleration or disable hardware acceleration (depending on the situation).
  • Avoid complex animations or gradients in CSS.
  • Use double-buffered rendering to reduce drawing jitter.

Multi-window Electron application (including splash page, main interface, and child windows)

Directory structure

my-electron-app/
├── package.json
├── main.js             # 主进程入口
├── preload.js          # 预加载脚本
├── splash.html         # 启动画面
├── index.html          # 主窗口页面
├── child.html          # 子窗口页面
└── renderer.js         # 渲染进程逻辑

main.js (main process)

Example

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

let mainWindow, splash, childWindow

function createSplash() {
  splash = new BrowserWindow({
    width: 400,
    height: 300,
    frame: false,
    transparent: true,
    alwaysOnTop: true,
  })
  splash.loadFile('splash.html')
}

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    show: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })

  mainWindow.loadFile('index.html')

  // Display after page load completes
  mainWindow.once('ready-to-show', () => {
    if (splash) splash.close()
    mainWindow.show()
  })

  mainWindow.on('closed', () => (mainWindow = null))
}

function createChildWindow() {
  childWindow = new BrowserWindow({
    parent: mainWindow,
    modal: true,
    width: 400,
    height: 300,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })
  childWindow.loadFile('child.html')
}

// Inter-process communication
ipcMain.on('open-child', () => {
  if (!childWindow) createChildWindow()
})

ipcMain.handle('get-app-info', () => {
  return {
    name: app.getName(),
    version: app.getVersion(),
  }
})

app.on('ready', () => {
  createSplash()
  setTimeout(() => createMainWindow(), 1500)
})

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

preload.js(preload script)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  openChild: () => ipcRenderer.send('open-child'),
  getAppInfo: () => ipcRenderer.invoke('get-app-info'),
})

index.html (main window)

Example

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>main window</title>
  <style>
    body { font-family: sans-serif; text-align: center; margin-top: 100px; }
    button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
  </style>
</head>
<body>
  <h1>Welcome to Electron Applications</h1>
  <p id="info"></p>
  <button id="open">Open subwindow</button>

  <script src="renderer.js"></script>
</body>
</html>

child.html (child window)

Example

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>child window</title>
  <style>
    body { font-family: sans-serif; text-align: center; margin-top: 100px; }
  </style>
</head>
<body>
  <h2>This is a child window</h2>
  <p>Can receive commands from the main window</p>
</body>
</html>

splash.html (Splash Screen)

Example

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>Loading...</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      background: linear-gradient(135deg, #8EC5FC, #E0C3FC);
      font-family: sans-serif;
    }
    h1 {
      font-size: 28px;
      color: #333;
    }
  </style>
</head>
<body>
  <h1>Application is starting, please wait...</h1>
</body>
</html>

renderer.js (render logic)

Example

const info = document.getElementById('info')
const btn = document.getElementById('open')

// Display application info
window.electronAPI.getAppInfo().then((appInfo) => {
  info.textContent =`Application name:${appInfo.name}| Version: ${appInfo.version}`
})

// Open child window
btn.addEventListener('click', () => {
  window.electronAPI.openChild()
})

The process after running is as follows:

  1. Show at startupSplash screen(splash.html)。
  2. After loading is complete, automatically close the splash page and displaymain window。
  3. The main window can be opened via a buttonchild window, demonstrating IPC communication functionality.
  4. The main window usesPreload securely exposes APIs, preventing direct access to Node.js.
  5. All windows useEvent listening、Delayed displayand other optimization strategies to avoid white screens and flickering.
other extensions