# Fix Electron White Screen of Death

# Fix Electron White Screen of Death: Problem → Solution → Tips

## Problem

The "White Screen of Death" (WSOD) is one of the most frustrating issues Electron developers encounter. Your application launches successfully, but instead of displaying your UI, users are greeted with a blank white screen. The app appears to be running—it's responsive to window controls and doesn't crash—but no content renders.

This issue is particularly maddening because it provides minimal debugging information. The application doesn't throw obvious errors, the process doesn't exit, and there's no clear indication of what went wrong. Users see a white void, developers see a mystery, and support tickets pile up.

The white screen typically appears in several scenarios: after a fresh build, following an update, when deploying to a new environment, or intermittently during runtime. Sometimes it works perfectly in development but fails in production. Other times, it works for some users but not others. This unpredictability makes the WSOD one of the most challenging Electron bugs to diagnose and resolve.

## Root Causes

Before jumping to solutions, understanding why the white screen occurs is essential. Several underlying issues can trigger this behavior:

**Preload Script Failures**: If your preload script crashes or fails to load, the renderer process may not initialize properly, resulting in a blank screen.

**IPC Communication Breakdown**: When the main process and renderer process can't communicate effectively, the renderer may fail to receive critical initialization data.

**Asset Loading Issues**: Missing or incorrectly patched asset paths prevent CSS, JavaScript, and HTML from loading properly.

**Timing Problems**: Race conditions where the window displays before content is ready to render.

**Context Isolation Conflicts**: Improper configuration of context isolation can prevent scripts from executing correctly.

**Unhandled Promise Rejections**: Silent failures in asynchronous operations that don't trigger visible errors.

## Solutions

### Solution 1: Enable Comprehensive Logging

Start by adding detailed logging throughout your application lifecycle:

```javascript
// main.js
const { app, BrowserWindow } = require('electron');
const isDev = require('electron-is-dev');

app.on('ready', () => {
  console.log('App ready event fired');
  
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      enableRemoteModule: false,
    }
  });

  mainWindow.webContents.on('did-start-loading', () => {
    console.log('Page started loading');
  });

  mainWindow.webContents.on('did-finish-load', () => {
    console.log('Page finished loading');
  });

  mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
    console.error(`Failed to load: ${errorCode} - ${errorDescription}`);
  });

  mainWindow.webContents.on('crashed', () => {
    console.error('Renderer process crashed');
  });

  mainWindow.loadFile('index.html');
});
```

### Solution 2: Verify Asset Paths

Incorrect asset paths are a common culprit. Ensure your paths work in both development and production:

```javascript
// main.js
const path = require('path');
const isDev = require('electron-is-dev');

const getAssetPath = (asset) => {
  if (isDev) {
    return path.join(__dirname, '../public', asset);
  }
  return path.join(__dirname, '../build', asset);
};

const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: getAssetPath('preload.js'),
  }
});

const startUrl = isDev
  ? 'http://localhost:3000'
  : `file://${path.join(__dirname, '../build/index.html')}`;

mainWindow.loadURL(startUrl);
```

### Solution 3: Handle Preload Script Errors

Wrap your preload script in error handling:

```javascript
// preload.js
const { contextBridge, ipcRenderer } = require('electron');

try {
  contextBridge.exposeInMainWorld('electron', {
    ipcRenderer: {
      send: (channel, data) => ipcRenderer.send(channel, data),
      on: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args)),
      invoke: (channel, data) => ipcRenderer.invoke(channel, data),
    }
  });
  console.log('Preload script loaded successfully');
} catch (error) {
  console.error('Preload script error:', error);
  // Notify main process
  ipcRenderer.send('preload-error', error.message);
}
```

### Solution 4: Implement Timeout Detection

Add a timeout mechanism to detect when content fails to load:

```javascript
// main.js
const mainWindow = new BrowserWindow(/* ... */);

const contentLoadTimeout = setTimeout(() => {
  console.error('Content failed to load within timeout period');
  mainWindow.webContents.openDevTools();
  mainWindow.loadFile('error.html');
}, 5000);

mainWindow.webContents.on('did-finish-load', () => {
  clearTimeout(contentLoadTimeout);
  console.log('Content loaded successfully');
});

mainWindow.webContents.on('did-fail-load', () => {
  clearTimeout(contentLoadTimeout);
});
```

### Solution 5: Add Error Boundary in Renderer

Implement error handling in your renderer process:

```javascript
// renderer.js
window.addEventListener('error', (event) => {
  console.error('Renderer error:', event.error);
  document.body.innerHTML = `<h1>Error: ${event.error.message}</h1>`;
});

window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);
  document.body.innerHTML = `<h1>Error: ${event.reason}</h1>`;
});

// Signal to main process that renderer is ready
window.electron?.ipcRenderer?.send('renderer-ready');
```

### Solution 6: Verify Context Isolation Configuration

Ensure your context isolation settings are correct:

```javascript
// main.js
const mainWindow = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,
    enableRemoteModule: false,
    preload: path.join(__dirname, 'preload.js'),
    sandbox: true,
  }
});
```

### Solution 7: Check Content Security Policy

Add CSP headers to your HTML:

```html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" 
        content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
  <title>My App</title>
</head>
<body>
  <div id="root"></div>
  <script src="renderer.js"></script>
</body>
</html>
```

## Tips for Prevention and Debugging

**Use DevTools in Production**: Temporarily enable DevTools to inspect what's happening:

```javascript
if (process.env.DEBUG_WSOD) {
  mainWindow.webContents.openDevTools();
}
```

**Implement Health Checks**: Create a simple health check endpoint:

```javascript
// main.js
ipcMain.handle('health-check', () => {
  return { status: 'ok', timestamp: Date.now() };
});
```

**Test Asset Loading**: Verify all assets load correctly by checking network requests in DevTools.

**Use Source Maps**: Enable source maps in production builds to get meaningful stack traces.

**Monitor Process Events**: Listen to all process events to catch silent failures:

```javascript
process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection at:', promise, 'reason:', reason);
});
```

**Version Your Assets**: Use versioning or hashing to prevent caching issues:

```javascript
const assetVersion = require('./package.json').version;
mainWindow.loadURL(`file://${path.join(__dirname, 'index.html')}?v=${assetVersion}`);
```

**Test Across Environments**: Reproduce the issue in different environments (Windows, macOS, Linux) as the WSOD can be platform-specific.

**Check Dependency Compatibility**: Ensure all dependencies are compatible with your Electron version, as version mismatches can cause silent failures.

## Conclusion

The Electron White Screen of Death is frustrating but solvable. By implementing comprehensive logging, verifying asset paths, handling errors gracefully, and following the debugging tips provided, you can identify and fix the root cause quickly. The key is to add visibility into your application's startup process and handle failures explicitly rather than letting them fail silently. With these strategies in place, you'll transform the mysterious white screen into a clear, actionable error message that points you toward the solution.
