BrowserWindow ist die zentrale Klasse für jedes Anwendungs-Fenster in Electron. Hier die Grundlagen — vom einfachen Fenster-Erzeugen über das Vermeiden des berüchtigten weißen Flackerns beim Start bis zu den wichtigsten Optionen für Größe, Position und Verhalten.
Minimal-Fenster
import { app, BrowserWindow } from 'electron';
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 1200,
height: 800
});
win.loadFile('index.html');
});Drei Zeilen — fertig ist das Fenster. Aber: das Fenster erscheint sofort und zeigt eine weiße Fläche, bis das HTML geladen ist. Lösung dazu in Abschnitt 03.
Wichtigste Konstruktor-Optionen
| Option | Default | Bedeutung |
|---|---|---|
width, height | 800 × 600 | Initial-Größe |
x, y | zentriert | Position auf dem Screen |
minWidth, minHeight | 0 | Untere Größen-Grenzen |
show | true | Sofort anzeigen — meist auf false setzen (siehe unten) |
frame | true | Native Fensterleiste (Titel, Buttons) |
titleBarStyle | default | macOS: hiddenInset für moderne Look |
resizable | true | User kann Größe ändern |
transparent | false | Hintergrund durchsichtig (für Custom-Designs) |
webPreferences | siehe unten | Sicherheits- und Verhaltens-Optionen |
const win = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 800,
minHeight: 600,
show: false,
backgroundColor: '#1a1a1a',
titleBarStyle: 'hiddenInset',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
sandbox: true,
nodeIntegration: false
}
});ready-to-show — kein weißes Flackern
Wenn show: true (Default) und loadFile lädt: Fenster ist offen, weiß, dann erscheint Inhalt. Sieht unprofessionell aus.
Lösung: show: false, dann auf ready-to-show warten und manuell zeigen.
const win = new BrowserWindow({
width: 1200,
height: 800,
show: false, // erstmal versteckt
backgroundColor: '#1a1a1a' // matcht den Theme — Fallback während Render
});
win.loadFile('index.html');
win.once('ready-to-show', () => {
win.show();
});ready-to-show feuert, wenn der Renderer den ersten Frame fertig hat. Kein Flackern, professioneller Look.
Inhalte laden — loadFile vs. loadURL
// Lokale HTML-Datei (typisch in der gepackten App)
win.loadFile('index.html');
win.loadFile('pages/settings.html');
// URL — typisch für Dev-Server während Entwicklung
if (process.env.NODE_ENV === 'development') {
win.loadURL('http://localhost:5173'); // Vite-Dev-Server
} else {
win.loadFile('dist/index.html');
}In Production lädst du fast immer aus dem App-Bundle (loadFile). Während Development zeigt loadURL mit dem Vite/Webpack-Dev-Server, damit HMR funktioniert.
Wichtige Methoden
win.show(); // Fenster zeigen
win.hide(); // verstecken (im Tray etc.)
win.close(); // schließen (löst close-Event aus)
win.destroy(); // hart killen, ohne Events
win.focus(); // in den Vordergrund holen
win.maximize(); // maximieren
win.minimize(); // minimieren
win.fullScreen = true; // Vollbild
win.setSize(1024, 768);
win.setPosition(100, 100);
win.setTitle('Neuer Titel');
win.reload(); // Inhalt neu ladenWichtige Events
| Event | Wann |
|---|---|
ready-to-show | Erster Render-Frame fertig |
show / hide | Sichtbarkeit geändert |
focus / blur | Fenster bekommt/verliert Fokus |
maximize / unmaximize | Maximier-Status geändert |
close | User klickt X — verhinderbar mit preventDefault() |
closed | Fenster ist weg — Cleanup-Zeitpunkt |
resize / move | Größe oder Position geändert |
win.on('close', async (event) => {
if (hasUnsavedChanges()) {
event.preventDefault();
const choice = await dialog.showMessageBox(win, {
type: 'question',
buttons: ['Speichern', 'Verwerfen', 'Abbrechen'],
message: 'Änderungen speichern?'
});
if (choice.response === 0) {
await save();
win.destroy();
} else if (choice.response === 1) {
win.destroy();
}
}
});preventDefault() blockt das Schließen — nach User-Entscheidung mit destroy() final schließen (sonst Endlos-Schleife).
Fenster-Position und -Größe persistieren
Pattern: User-Größe merken und beim nächsten Start wiederherstellen.
import Store from 'electron-store';
const store = new Store();
const lastBounds = store.get('window-bounds', { width: 1200, height: 800 });
const win = new BrowserWindow({
...lastBounds,
show: false
});
// Position/Größe beim Schließen merken
win.on('close', () => {
store.set('window-bounds', win.getBounds());
});getBounds() liefert { x, y, width, height } — perfekt zum Speichern. Mehr zu electron-store im Datenpersistenz-Kapitel.
Häufige Stolperfallen
Default show: true macht weißes Flackern.
Beim Start: Fenster offen, weißer Hintergrund, dann Inhalt. Mit show: false + ready-to-show umgehen. Plus backgroundColor setzen, damit der Renderer-Fallback bei langsamem Laden nicht weiß ist.
close vs. closed — wichtige Unterscheidung.
close: vor dem Schließen, abbrechbar. closed: nach dem Schließen, Window-Referenz ist tot. Cleanup, das auf Window zugreift, gehört in close (vor destroy); Cleanup, das nichts mehr braucht, in closed.
Mehrere Fenster gleichzeitig — Referenzen managen.
Bei Multi-Window-Apps NICHT Window-Referenzen in einer Variablen speichern, die der Garbage Collector räumen könnte. Set oder Array verwenden. In closed aus dem Set entfernen.
destroy() umgeht Events — vorsichtig nutzen.
Während close() Events feuert (close, closed) und abgebrochen werden kann, killt destroy() das Fenster sofort, ohne close-Event. Praktisch für „User hat gespeichert, jetzt wirklich schließen", aber nicht als Default verwenden.
BrowserView ist deprecated.
Wer mehrere Renderer in einem Fenster braucht (Browser-Tabs, embed-Views): WebContentsView ab Electron 28+. BrowserView funktioniert noch, ist aber Auslauf-Modell.
setBounds / getBounds arbeitet in Screen-Pixeln.
Auf High-DPI-Displays kann das verwirren — getContentBounds() arbeitet in CSS-Pixeln (DPI-aware), getBounds() in Screen-Pixeln. Für UI-Layout meist ContentBounds, für Position/Persistenz Bounds.
Weiterführende Ressourcen
Externe Quellen
- BrowserWindow — komplette API-Referenz
- Window Customization — Frame-less, transparent, custom title bar
- Showing Windows Gracefully