# How to Implement Multi-Window Apps with Model-Declared Windows in the Native SDK

> Implement multi-window apps with model-declared windows in Native SDK by declaring ShellWindow records in your app.zon manifest. The runtime creates native platform windows and wires ShellView hierarchies.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**You implement multi-window apps with model-declared windows in the Native SDK by declaring `ShellWindow` records in your `app.zon` manifest, which the runtime reads to create native platform windows and wire their `ShellView` hierarchies automatically.**

The `vercel-labs/native` repository provides a declarative multi-window API that lets you describe every window in your application’s manifest. By modeling windows as data rather than platform-specific code, you can define multiple independent windows with custom sizes, title-bar styles, and view layouts that persist across app launches.

## Declarative Multi-Window Architecture in the Native SDK

The Native SDK treats every application window as a **`ShellWindow`** struct that lives in `app.zon`. Because the window model is pure data, the runtime can create platform-native windows, lay out their views, and restore their state without imperative UI code.

### Core Components

- **`ShellWindow`** — Declares the window’s `label`, `width`, `height`, `titlebar` style, and its array of `ShellView`s. Defined in [`src/primitives/app_manifest/types.zig`](https://github.com/vercel-labs/native/blob/main/src/primitives/app_manifest/types.zig).
- **`ShellConfig`** — The top-level container that exposes the `windows` array parsed from the manifest. Same file as above.
- **`Runtime.createShellWindow`** — Consumes a `ShellWindow` record and builds the underlying `platform.WindowInfo`. Located in [`src/runtime/window_views.zig`](https://github.com/vercel-labs/native/blob/main/src/runtime/window_views.zig).
- **`Runtime.applyShellViews`** — Maps the `ShellView` array into native canvas layers or webviews after the window exists. Same file as above.
- **`ShellLayout`** — Translates layout hints (`x`, `y`, `edge`, `width`, `height`) into concrete `RectF` bounds. Found in [`src/runtime/shell_layout.zig`](https://github.com/vercel-labs/native/blob/main/src/runtime/shell_layout.zig).
- **`WindowStorageMethods.updateWindowState`** — Persists geometry and visibility across launches. Defined in [`src/window_state/root.zig`](https://github.com/vercel-labs/native/blob/main/src/window_state/root.zig).

### Runtime Lifecycle

1. **Manifest parsing.** On startup, the SDK loads `app.zon` and reads the `ShellConfig.windows` slice.
2. **Startup window.** The first element of the `windows` array becomes the host window created before the scene loads.
3. **Model-declared windows.** After the scene loads, the runtime invokes `Runtime.createShellWindow` for each remaining `ShellWindow` record.
4. **View creation.** The runtime validates the view hierarchy and instantiates native views according to each `ShellView` kind.
5. **State persistence.** Resizes, moves, and close events are forwarded to `WindowStorageMethods.updateWindowState` so the next launch restores the exact layout.

## Declaring Windows in `app.zon`

Window declarations live inside the `.shell` block of your manifest. Each window carries a unique label, dimensions, title-bar configuration, and a list of views.

```zon
.shell = .{
  windows = .{
    .{
      label = "main",
      width = 720,
      height = 480,
      titlebar = .standard,
      views = &.{
        .{ label = "canvas", kind = .canvas },
        .{ label = "sidebar", kind = .canvas, edge = .right, width = 200 },
      },
    },
    .{
      label = "settings",
      width = 500,
      height = 400,
      title = "Settings",
      titlebar = .standard,
      views = &.{
        .{ label = "web", kind = .webview, url = "https://example.com/settings" },
      },
    },
  },
};

```

- The `label` uniquely identifies the window for effects and programmatic commands.
- `titlebar` controls the OS chrome and accepts values such as `.standard` or `.hidden`.
- Each **`ShellView`** declares its `kind` (`canvas`, `webview`, `image`, etc.) and optional layout hints that `ShellLayout` resolves at runtime.

## Runtime Window Creation for Multi-Window Apps

Windows are not just static manifest entries; you can construct and open them dynamically from Zig using the same `ShellWindow` model.

### Opening a Secondary Window Programmatically

The public API entry point is `Runtime.createShellWindow`, which mirrors the manifest record. When the window’s views already declare their own content—for example a webview with a URL—you pass `null` for the initial `WebViewSource`.

```zig
const native_sdk = @import("native_sdk");

fn openSettings(runtime: *native_sdk.Runtime) !void {
    const settings_win = native_sdk.ShellWindow{
        .label = "settings",
        .title = "App Settings",
        .width = 500,
        .height = 400,
        .views = &.{ .{
            .label = "web",
            .kind = .webview,
            .url = "https://example.com/settings",
        } },
    };

    _ = try runtime.createShellWindow(settings_win, null);
}

```

Under the hood, `createShellWindow` delegates to `Runtime.createShellWindowWithSourcePolicy`, which allocates the native platform window and then invokes the view-creation pipeline. For pure-canvas windows that never need a webview, the SDK exposes `createSourcelessShellWindow` instead.

### Targeting and Closing Windows by Label

To close a specific window from model code, use `effectsWindowIdByLabel` to resolve the manifest label to a live `WindowId`, then call `closeWindow`.

```zig
fn closeWindowByLabel(runtime: *native_sdk.Runtime, label: []const u8) bool {
    const win_id = native_sdk.effectsWindowIdByLabel(runtime, label) orelse return false;
    runtime.closeWindow(win_id) catch return false;
    return true;
}

```

This pattern is the canonical way to target model-declared windows from update loops or command handlers.

## Window State Persistence in the Native SDK

The SDK automatically saves and restores window geometry. Whenever a window is resized, moved, or closed, the runtime calls **`WindowStorageMethods.updateWindowState`** in `src/window_state/root.zig`. On the next launch, the SDK reads the saved state and reapplies it, ensuring users see their previous layout rather than the default manifest values.

## Real-World Example: The Notes Demo

The repository’s notes example demonstrates a single-window setup that you can extend to multiple windows. It declares a `ShellConfig` in Zig and assigns it to `shell_scene`.

```zig
const shell_windows = [_]native_sdk.ShellWindow{.{
    .label = "main",
    .title = "Notes",
    .width = 720,
    .height = 480,
    .views = &.{
        .{ .label = "canvas", .kind = .canvas },
        .{ .label = "toolbar", .kind = .canvas, .edge = .top, .height = 44 },
    },
}};
pub const shell_scene: native_sdk.ShellConfig = .{ .windows = &shell_windows };

```

You can find this declaration in [`examples/notes/src/main.zig`](https://github.com/vercel-labs/native/blob/main/examples/notes/src/main.zig). Expanding the `shell_windows` array with additional `ShellWindow` entries is all that is required to turn this into a multi-window app.

## Summary

- **`ShellWindow`** records in `app.zon` describe windows declaratively, including size, position, title-bar style, and view hierarchy.
- The first window in the `ShellConfig.windows` array is treated as the startup window; subsequent entries are created via `Runtime.createShellWindow`.
- Views are wired automatically by the runtime using the `ShellView` kind and layout hints resolved through `ShellLayout`.
- Pure-canvas windows can use `createSourcelessShellWindow` to bypass webview creation entirely.
- Window state is transparently persisted through `WindowStorageMethods.updateWindowState` in `src/window_state/root.zig`.

## Frequently Asked Questions

### Can I mix canvas-only windows and webview windows in the same app?

Yes. The manifest accepts any combination of `canvas`, `webview`, and other view kinds across multiple `ShellWindow` declarations. For windows that do not need a webview, the SDK provides `createSourcelessShellWindow`, which disables the webview layer and hosts only native canvas views.

### How does the SDK decide which window opens first?

The runtime treats the **first element** of the `ShellConfig.windows` array as the startup window. The host creates this window before the scene finishes loading. All remaining windows in the array are instantiated afterward through the standard `createShellWindow` flow.

### What layout properties are available for views inside a window?

Each `ShellView` supports layout hints such as `x`, `y`, `width`, `height`, and `edge`. The `ShellLayout` engine in `src/runtime/shell_layout.zig` converts these hints into concrete `RectF` bounds relative to the window frame, so you can build sidebars, toolbars, and centered canvases without manual geometry calculations.

### How do I target a specific window from my Zig code?

Always use the window’s manifest `label`. The SDK exposes `effectsWindowIdByLabel`, which scans the live window table and returns the platform `WindowId`. Once you have the ID, you can call runtime methods such as `closeWindow` or send custom commands to that specific window.