Zap Studio

Window State

Automatically remember window size, position, and state across app restarts in Local.ts.

Local.ts uses the Tauri window state plugin to automatically remember your window size, position, and state across app restarts.

This creates a polished desktop experience where users don't have to resize their window every time they open the app.

What Gets Saved

The window state plugin tracks:

PropertyDescription
SizeWindow width and height
PositionWindow X and Y coordinates
MaximizedWhether the window is maximized
FullscreenWhether the window is in fullscreen
DecorationsWhether window decorations are visible

How It Works

Initialization

The plugin is initialized during app setup and restores state for all windows:

// src-tauri/src/lib.rs
.setup(|app| {
    plugins::logging::init(app);

    // Initialize window state plugin
    #[cfg(desktop)]
    plugins::window_state::init(app)?;

    // ... other setup code
    Ok(())
})

The init function registers the plugin and restores saved state.

Saving State

Window state is saved automatically when the window closes:

// src-tauri/src/lib.rs
.on_window_event(|window, event| {
    #[cfg(desktop)]
    if let tauri::WindowEvent::CloseRequested { .. } = event {
        plugins::window_state::on_close_requested(window);
    }
})

The on_close_requested function handles the save operation.

Customizing What's Saved

You can choose which properties to persist by modifying the StateFlags in the plugin module:

// src-tauri/src/plugins/window_state.rs
pub fn init<R: Runtime>(app: &App<R>) -> Result<(), Box<dyn std::error::Error>> {
    // ... plugin registration ...

    // Only restore size and position
    let flags = StateFlags::SIZE | StateFlags::POSITION;

    for (label, window) in windows {
        if let Err(err) = window.restore_state(flags) {
            log::warn!("Failed to restore state for window '{}': {}", label, err);
        }
    }

    Ok(())
}

pub fn on_close_requested<R: Runtime>(window: &Window<R>) {
    // Only save size and position
    let flags = StateFlags::SIZE | StateFlags::POSITION;

    if let Err(err) = window.app_handle().save_window_state(flags) {
        log::warn!(
            "Failed to save window state for '{}': {}",
            window.label(),
            err
        );
    }
}

Available flags:

FlagWhat It Saves
StateFlags::SIZEWindow dimensions
StateFlags::POSITIONWindow location
StateFlags::MAXIMIZEDMaximized state
StateFlags::VISIBLEVisibility state
StateFlags::DECORATIONSWindow decorations
StateFlags::FULLSCREENFullscreen state
StateFlags::all()All of the above

Default Window Size

Set default dimensions in src-tauri/tauri.conf.json for first launch before any state is saved:

{
  "windows": [{
    "title": "Local.ts",
    "label": "main",
    "visible": false,
    "width": 1280,
    "height": 720,
    "center": true
  }]
}

These defaults are used only on first launch.

Error Handling

The window state plugin uses proper error handling with logging:

  • Restore failures are logged as warnings but don't prevent the app from starting
  • Save failures are logged as warnings to help debug state persistence issues
  • Window labels are included in error messages for easier troubleshooting

This ensures that state management issues won't crash your application.

Saving State Periodically

Instead of (or in addition to) saving on close, you can save state periodically by adding a function to the plugin:

// src-tauri/src/plugins/window_state.rs
use std::time::Duration;

pub fn start_periodic_save<R: Runtime>(app: &App<R>) {
    let app_handle = app.handle().clone();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep(Duration::from_secs(300)); // 5 minutes
            if let Err(err) = app_handle.save_window_state(StateFlags::all()) {
                log::warn!("Failed to save window state periodically: {}", err);
            }
        }
    });
}

Then call it from your setup:

// src-tauri/src/lib.rs
.setup(|app| {
    #[cfg(desktop)]
    {
        plugins::window_state::init(app)?;
        plugins::window_state::start_periodic_save(app);
    }

    Ok(())
})

This protects against state loss if the app crashes.

The spawned thread runs for the application's entire lifetime — it will only exit when the application terminates, so make sure any resources accessed from that thread are safe to use for the duration of the process.

Removing Window State

If you don't need window state persistence:

  1. Remove the plugin module at src-tauri/src/plugins/window_state.rs

  2. Remove the initialization from src-tauri/src/lib.rs:

    - #[cfg(desktop)]
    - plugins::window_state::init(app)?;
  3. Remove the window event handler from src-tauri/src/lib.rs:

    - .on_window_event(|window, event| {
    -     #[cfg(desktop)]
    -     if let tauri::WindowEvent::CloseRequested { .. } = event {
    -         plugins::window_state::on_close_requested(window);
    -     }
    - })
  4. Remove the dependency from src-tauri/Cargo.toml:

    - tauri-plugin-window-state = "2"
  5. Remove permissions from src-tauri/capabilities/default.json:

    - "window-state:default"
Edit on GitHub

Last updated on

On this page