5.7k

macOS · Linux · Windows · iOS · Android

Toolkit for building native desktop apps

Write your interface in native markup and Zig. The toolkit's own engine renders it into real OS windows — no browser, no WebView.

$ npm install -g @native-sdk/cli
The Soundboard example app rendered by the Native SDK engine (light theme)
<6 MB

Every app on this page — engine, widgets, renderer — as one static release binary.

~100 ms

From launch to the first frame on the glass — 71–131 ms warm across these apps on macOS arm64.

0

Embedded browsers, script engines, or interpreters inside those binaries.

Measured in this repository: zig build -Doptimize=ReleaseFast on macOS arm64; launch is process spawn to first presented frame.

Principles

Beautiful by default. Customizable by design.

Native SDK exists because expressive UI and native performance should not be competing goals. Developers often choose web-based runtimes because they offer freedom, speed and control over the product experience. But that freedom often comes with a heavy runtime. Native SDK keeps the expressive authoring model and replaces the runtime with native rendering.

The Soundboard example app rendered by the Native SDK engine (light theme): a clean music library with album covers and a playback bar
The Deck example app rendered by the Native SDK engine: the same music player rebuilt as a fixed 512 by 264 chromeless hardware unit in cream enamel with smoked-glass display bays, a phosphor seven-segment timecode, a spectrum analyzer, and a rotary volume knob

The same toolkit. The same player. Two identities.

Every difference between examples/soundboard and examples/deck is design tokens and a chrome pass — same widgets, same engine. One is an airy app window that follows the site theme; the other is a dense 512×264 enamel-and-glass hardware unit with one finish by design.

Beautiful by default

Great software should not start from a blank slate.

Customizable by design

Your app should have its own identity, not ours.

Native from the start

Every interface is rendered without a browser or WebView.

Predictable state

State changes should be explicit, inspectable and easy to reason about.

Simple authoring

Interfaces should be easy to read, easy to write and easy to generate.

AI is part of the workflow

Native SDK is designed for a world where humans and AI agents build software together.

Predictable by design

Events. Messages. State. Interface.

Events produce messages, messages update state, and state renders the interface — simple to debug, simple to maintain, and simple for AI to generate. This is examples/ui-inbox from the repository: the whole UI is one declarative view, and one update function is the only place state changes. Mistakes in a view are compile errors with line and column, and in dev you edit the view while the app runs, keeping state.

src/inbox.native
<column background="background">
  <row height="{header_height}" padding="12" gap="10" cross="center"
       background="surface" window-drag="true" label="Inbox header">
    <spacer width="{chrome_leading}" />
    <spacer grow="1" />
    <if test="{doneCount}">
      <button variant="ghost" on-press="clear_done">Clear done</button>
    </if>
  </row>
  <separator />
  <column grow="1" gap="12" padding="16">
    <row gap="8" cross="center">
      <text-field text="{draft}" placeholder="New task…"
                  on-input="draft_edit" on-submit="add" grow="1" />
      <button variant="primary" on-press="add">Add task</button>
    </row>
    <tabs gap="8">
      <for each="filters" as="f">
        <button size="sm" selected="{f == filter}"
                on-press="set_filter:{f}">{f}</button>
      </for>
    </tabs>
    <scroll grow="1">
      <column gap="2">
        <for each="visible" key="id" as="t">
          <row gap="8" padding="6" cross="center">
            <checkbox checked="{t.done}" on-toggle="toggle:{t.id}"
                      label="Done" />
            <text grow="1">{t.title}</text>
          </row>
        </for>
      </column>
    </scroll>
  </column>
  <status-bar>{openCount} open · {doneCount} done</status-bar>
</column>
src/main.zig
pub const Msg = union(enum) {
    add,
    toggle: u32,
    set_filter: Filter,
    clear_done,
    draft_edit: canvas.TextInputEvent,
    chrome_changed: native_sdk.WindowChrome,
};

pub fn update(model: *Model, msg: Msg) void {
    switch (msg) {
        .add => {
            if (model.draftEmpty()) {
                model.addGeneratedTask();
            } else {
                model.addTask(std.mem.trim(u8, model.draft(), " "));
                model.draft_buffer.clear();
            }
        },
        .toggle => |id| if (model.taskById(id)) |task| {
            task.done = !task.done;
        },
        .set_filter => |filter| model.filter = filter,
        .clear_done => model.clearDone(),
        .draft_edit => |edit| model.draft_buffer.apply(edit),
        .chrome_changed => |chrome| {
            model.chrome_leading = chrome.insets.left;
            model.header_height =
                @max(header_natural_height, chrome.insets.top);
        },
    }
}
The ui-inbox example app running in a native macOS window: the window controls share the header band with a Clear done action, above a text field, filter tabs, a checklist of tasks, and a status bar
Built from the source above and captured running on macOS. The pixels come from Native SDK’s engine; the window and scroll physics come from the OS.

Built for modern apps

Seven real apps, in the repo

Dashboards, editors, tools, internal apps, creative software — every screenshot is rendered by Native SDK’s deterministic engine from the example apps in examples/, the same state captured once per color scheme. Flip the site theme and the apps flip with it — deck alone stays dark, by design.

The Markdown Viewer example app rendered by the Native SDK engine (light theme)

A split-pane editor whose preview is native widgets.

Headings, tables, task lists, links, and blockquotes on the right are ordinary widgets rendered live from the editor on the left — keystroke for keystroke, with no WebView and no HTML.

Native from the start

Feels native because it is

Native SDK owns its renderer — no embedded browser, no heavy runtime pretending to be native. One engine draws every widget into real OS windows, and the parts users touch stay with the operating system: scrolling carries OS momentum, menus are real menus, and the tray is the real tray.

Native UI Guide →
OS scroll physics

momentum and rubber-band overscroll on macOS

Context menus

declare one menu in markup or Zig; the OS presents it natively, with an automatic anchored fallback

Menu bar & tray

app menus and menu-bar extras driven by the model

Dialogs & file drop

native open/save panels and drop events as messages

IME composition

real text input on macOS, Linux, and Windows

HiDPI rendering

crisp scale-factor-aware pixels on every display

any agent, any running app
$ native automate wait$ native automate snapshotrole=button name="Add task" …$ native automate widget-click canvas 3$ native automate assert 'gpu_nonblank=true'$ native automate screenshot

AI is part of the workflow

Built to be written by AI agents

Declarative markup and one typed update function make a surface agents author reliably — and the repository ships an agent skill that teaches all of it. Every app embeds an automation server, so any agent can see and drive the running window: snapshots, assertions, input, screenshots. An eval harness hands a clean agent a scaffolded workspace and grades the result — builds, markup checks, live snapshots, and an LLM judge.

Automation →

One binary

The whole app is one small file

Markup compiles into the executable, so release builds carry no parser, no interpreter, and no scripting engine — just your logic and the engine, linking the system’s own frameworks. Effects run HTTP fetches, process spawns, file I/O, and timers off the loop; results come back into update as plain messages. And when part of your product is the web, WebView panes coexist with the canvas in the same window.

Packaging →
examples — release builds
$ zig build -Doptimize=ReleaseFast$ ls -lh */zig-out/bin3.6M calculator4.2M deck3.6M feed3.5M markdown-viewer3.5M notes5.7M soundboard3.7M system-monitor

Cross-platform

One SDK, desktop and mobile

One codebase compiles for macOS, Linux, Windows, iOS, and Android. Desktop is the mature surface; mobile is experimental — verified on the simulator and emulator, with APIs and tooling still evolving. These statuses describe what ships and is verified today — not a roadmap.

macOS

Native

Metal presentation, OS scroll physics, native context menus, menus, tray, and dialogs. The primary development platform.

Linux

Software presentation

GTK windows driven by the deterministic software renderer, with pointer, keyboard, scroll, IME composition, and HiDPI.

Windows

Software presentation

Win32 host with IME composition. Cross-compiled and exercised in CI under Wine, including real input injection.

iOS

Experimental

Apps compile into an embed library and present via CAMetalLayer. Verified on the iOS Simulator; device support is in progress.

Android

Experimental

Cross-compiles with the full embed ABI and a NativeActivity shim. On-device runs are not yet verified.

WebViews

Coexisting

System WebView apps and panes on macOS, Linux, and Windows; bundled Chromium (CEF) on macOS.

Full Support Matrix →

Build something native

Scaffold an app, open a real window, and edit the view while it runs.

terminal
$ native init my_app$ cd my_app && native deva real window opens — edit src/app.native while it runs