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

JavaScript
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

OptionDefaultBedeutung
width, height800 × 600Initial-Größe
x, yzentriertPosition auf dem Screen
minWidth, minHeight0Untere Größen-Grenzen
showtrueSofort anzeigen — meist auf false setzen (siehe unten)
frametrueNative Fensterleiste (Titel, Buttons)
titleBarStyledefaultmacOS: hiddenInset für moderne Look
resizabletrueUser kann Größe ändern
transparentfalseHintergrund durchsichtig (für Custom-Designs)
webPreferencessiehe untenSicherheits- und Verhaltens-Optionen
JavaScript Sinnvolle Defaults
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.

JavaScript Saubere Anzeige
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

JavaScript
// 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

JavaScript
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 laden

Wichtige Events

EventWann
ready-to-showErster Render-Frame fertig
show / hideSichtbarkeit geändert
focus / blurFenster bekommt/verliert Fokus
maximize / unmaximizeMaximier-Status geändert
closeUser klickt X — verhinderbar mit preventDefault()
closedFenster ist weg — Cleanup-Zeitpunkt
resize / moveGröße oder Position geändert
JavaScript Close abfangen für unsaved changes
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.

JavaScript
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

/ Weiter

Zurück zu Grundlagen

Zur Übersicht