Native SDK: a Zig engine that renders declarative UI into real OS windows
Toolkit for building native desktop apps
At a glance
- What is it?
- Vercel Labs' Native SDK compiles .native markup and TypeScript (or Zig) into a single desktop binary with no browser or WebView. It is promising for small state-driven apps and awkward for anything that depends on web platform libraries.
- Who is it for?
- Adopt Native SDK if you are building a small, state-driven desktop tool and you accept that the component catalog and the Zig engine define your ceiling. Do not adopt it if your app depends on npm UI libraries, a WebView, or a rendering layer you can patch yourself.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Zig, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Native SDK actually replaces
Most desktop apps that want expressive UI reach for a web runtime. Electron and Tauri both put a browser engine in the shipped artifact, which is what gives you the npm ecosystem and CSS. Native SDK takes the opposite position: the README says the project exists because "expressive UI and native performance should not be competing goals," and it keeps the authoring model while removing the runtime. There is no browser, no WebView and no JavaScript runtime in the binary. The engine draws every pixel into real OS windows.
The audience is narrow and specific. It is for developers who want to write a desktop UI declaratively, keep state in one pure function, and ship a binary measured in a few megabytes rather than a bundled Chromium. The README states that the scaffolded counter app builds to a single binary a few megabytes small. It is not for teams whose product depends on a specific npm component library, or on web APIs the engine has not implemented. The repository is a Vercel Labs experiment, not a finished platform, and that framing should shape how you read the feature list.
Views, messages, and a pure update function
The architecture is a strict unidirectional loop. Views are declarative markup in .native files. Logic is plain TypeScript compiled to native code at build time, or Zig if you prefer. Events produce messages, messages update state, and state renders the interface. The README is explicit that markup can bind and dispatch but never mutate, and that the update function is the only place state changes.
The view layer is elements, flex layout, {bindings} and expressions. The README gives selected="{f == filter}" as an example of an expression inside a binding. The README also lists the files that make up a scaffolded app: src/app.native for the view, src/core.ts for the model, message union and update function, and a manifest. The README describes this as "three files of truth" with no build config.
Because views are validated against the app's actual Model and Msg types, native check can reject a binding that references a field you do not have, an iterable that is not iterable, or a message tag that does not exist. Errors are reported with file:line:column. That type coupling between markup and logic is the most interesting design decision in the project. It is also the constraint: your view file is not portable to another framework, and the checker only knows about the types you declared.
Installing the CLI and running the counter
The README's quick start is two steps. First install the CLI globally from npm, then scaffold and run an app. The scaffolded project opens a native window with a working counter, and the README states the whole app is three files with no build config.
npm install -g @native-sdk/cli
native init my_app
cd my_app
native devAfter native dev starts, a window opens showing the counter. The README says editing src/app.native while native dev runs updates the window in place and keeps your state, so you can change layout or styling without losing the current count.
The counter row in src/app.native is a flex row with a gap, centered on both axes, containing two buttons and a text node bound to {count}. Each button dispatches a message by name.
<row gap="8" main="center" cross="center" grow="1">
<button variant="secondary" on-press="decrement">-</button>
<text>{count}</text>
<button variant="primary" on-press="increment">+</button>
</row>The logic side is a Model interface, a Msg union, and one update function that returns a new model for each message kind. The README shows the increment, decrement and reset cases, each returning a spread of the previous model with a changed count.
export function update(model: Model, msg: Msg): Model {
switch (msg.kind) {
case "increment":
return { ...model, count: model.count + 1 };
case "decrement":
return { ...model, count: model.count - 1 };
case "reset":
return { ...model, count: 0 };
}
}Two more commands matter day to day. native dev --core runs the TypeScript core under node for instant logic checks without the window, and native check validates the core and every view in milliseconds without building. native build produces an optimized release binary. If you would rather write the core in Zig, native init my_app --template zig-core scaffolds the same app with src/main.zig, and the README says it is the same loop and the same runtime.
The component catalog is the ceiling
The README lists the built-in catalog as buttons, tabs, text fields, dialogs, charts and virtual lists, among others, with typography, spacing and color already decided. That is a real benefit for a tool that needs to look intentional on first launch, and it is why the scaffolded app does not start from a blank slate.
It is also the sharpest limitation. If your design needs a widget the catalog does not ship, you are not reaching for an npm package; you are either composing something from the primitives or working on the engine. The README does not document an extension API for third-party components, and it does not describe how to register a custom widget. Treat the catalog as the boundary of what you can build without touching Zig.
Theming is the escape hatch the README does emphasize. Styling is design tokens end to end: color, radius and typography resolve by name, re-resolve live when the theme changes, and can be replaced wholesale. The README points at examples/soundboard and examples/deck as the same music player separated only by tokens and a chrome pass. If your differentiation is visual rather than structural, tokens cover more ground than the widget list suggests. If it is structural, they do not.
Automation, replay, and where the loop breaks down
Every app embeds an automation server, according to the README. An agent can read accessibility snapshots, drive widgets, assert on live state, and take deterministic screenshots of the running window. The README also states that native automate record journals a session and replay reproduces it headlessly, verified frame by frame against state fingerprints. Because update is a pure function, a recorded sequence of messages should reproduce the same state, which is what makes the fingerprint comparison meaningful rather than decorative.
The failure modes are worth naming. Determinism holds only while state lives in the model. Any value that comes from the clock, the filesystem, the network or a random source will not reproduce under replay unless you route it through a message, and the README does not document a mechanism for injecting those sources. A recording that passes today can diverge tomorrow if the app reads the environment directly.
The second failure mode is the Zig core. The README presents Zig as first-class by choice, with the same loop and the same runtime. That is true of the architecture, but it is not true of the tooling: native dev --core runs the TypeScript core under node, and the README does not describe an equivalent fast path for a Zig core. Choosing the zig-core template trades the instant logic check for direct access to the engine. That is a reasonable trade for some apps and a bad one for others, and it is not framed that way in the README.
The third is platform scope. The repository contains examples/android, examples/ios, examples/browser and examples/mobile-shell, so the project is clearly reaching past the desktop, but the README describes Native SDK as a toolkit for native desktop applications and does not document the state of the mobile or browser targets. Do not read the presence of an example directory as a support commitment.
How it compares to Tauri and Flutter
Tauri is the closest comparison because both projects reject a bundled Chromium. The difference is what they keep. Tauri keeps the web platform: your UI is HTML, CSS and JavaScript running in the system WebView, and your backend is Rust. You inherit the npm ecosystem and CSS, and you inherit the WebView's rendering differences across operating systems. Native SDK keeps the declarative authoring model but replaces the WebView with its own Zig engine, so rendering is consistent and the binary is small, at the cost of the web platform and its libraries.
Flutter makes the opposite bet from Tauri and a similar one to Native SDK: it ships its own rendering engine and draws every pixel itself. The difference is the language and the layer you can reach. Flutter is Dart with its own widget tree, and the ecosystem is large enough that most widgets already exist. Native SDK is TypeScript or Zig with a smaller catalog, and the README does not describe a plugin system for closing that gap. If your team already knows Dart, Flutter is the lower-risk path to the same architectural idea.
The honest summary is that Native SDK is the smallest of the three in scope and the most opinionated about state. It asks you to accept a pure update function and a fixed catalog in exchange for a binary with no runtime inside it.
Licence, releases, and the cost of keeping up
Native SDK is Apache-2.0, which permits commercial use, modification and redistribution provided you preserve the notices and state your changes. For a desktop app you ship to users, that is a permissive arrangement, and it does not carry the source-disclosure obligations of a copyleft licence. This is a description of the licence text, not legal advice; if you are redistributing a modified engine, have counsel read the NOTICE and patent clauses.
The maintenance picture is active. The last push was on 2026-09-16, and the most recent release listed is v0.10.1 on 2026-08-24, following v0.10.0 the same day and v0.9.5 on 2026-08-18. A minor version bump roughly every week or two means the surface is still moving. The repository carries a CHANGELOG.md and a RELEASING.md, so the project documents its own release process, but the README does not document a migration path between minor versions. Budget for reading the changelog on each upgrade rather than assuming compatibility.
The upgrade cost is concentrated in two places. Markup that binds to your Model and Msg will fail native check loudly if a type changes, which is the good case. The bad case is anything that depends on token names, catalog component props, or the automation protocol, because the README does not describe a stability guarantee for those. The project describes itself as a Vercel Labs experiment, and that label is the most accurate guide to how much churn to expect.
Editorial conclusion
Adopt Native SDK if you are building a small, state-driven desktop tool and you accept that the component catalog and the Zig engine define your ceiling. Do not adopt it if your app depends on npm UI libraries, a WebView, or a rendering layer you can patch yourself. Before committing, install @native-sdk/cli, run native init and native check on your own model, open examples/soundboard and examples/deck to see how far token-level theming goes, and confirm that the built-in catalog covers your widget list. The Apache-2.0 licence lets you fork, but the engine and the catalog are the parts you would have to maintain.
Frequently asked questions
How do I install Native SDK?
Install the CLI globally from npm with npm install -g @native-sdk/cli, then run native init my_app and native dev. The README's quick start uses exactly those commands.
How do I use Native SDK to create an app?
Run native init my_app to scaffold the project, then native dev to open a window with a working counter. From there you edit src/app.native for the view and src/core.ts for the model and update function.
Does Native SDK use a WebView?
No. The README states that the engine draws every pixel into real OS windows with no browser, no WebView and no JavaScript runtime in the binary.
Can I write the core logic in Zig instead of TypeScript?
Yes. native init my_app --template zig-core scaffolds the same app with src/main.zig, and the README describes it as the same loop and the same runtime.
What does native check do?
It validates the core and every view in milliseconds without building, and reports bindings, iterables and message tags with file:line:column errors. The README says it checks views against your app's actual Model and Msg.
What licence is Native SDK released under?
Apache-2.0, per the repository's LICENSE file and the badge in the README.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vercel-labs-native)