Zero-Native: Build Native Desktop Apps with Zig and Web UI (Vercel Labs)
Zero-Native: Build Native Desktop Apps with Zig and Web UI 🚀
What is it? Zero-native is an open-source (Apache 2.0) framework from Vercel Labs that lets you build native desktop applications using Zig as the native shell and your favorite web framework (Next.js, React, Svelte, Vue) as the UI layer. The result: tiny binaries, minimal memory usage, and instant rebuilds.
Why it's trending: Zero-native hit 3,600+ GitHub stars in just over a week because it solves a long-standing tension in desktop development. Electron apps are easy to build but bloated (200MB+ for a counter app). Native toolkits like SwiftUI or GTK produce small binaries but require learning platform-specific APIs and don't use web frontend skills. Zero-native splits the difference: your UI is the web stack you already know, while the native shell is a lean Zig binary that uses the system's built-in WebView (WKWebView on macOS, WebKitGTK on Linux) or an optional bundled Chromium via CEF.
📋 Prerequisites
Before we start, make sure you have:
- Node.js 18+ and npm installed
- Zig 0.14+ installed (ziglang.org/download)
- A frontend framework preference: Next.js, React, Svelte, or Vue
- macOS 11+, Linux, or Windows for building
🧠 Architecture Overview
Here's how the zero-native stack is structured:
The architecture works in a clear top-to-bottom flow:
-
Developer & CLI — You install the
zero-nativeCLI via npm and runzero-native init my_app --frontend <framework>. The CLI generates a complete project with a Zig native shell and a frontend scaffold. -
app.zon — The app manifest declares metadata, icons, window config, web engine choice, security policies, and bridge permissions. The CLI generates it and reads it back at build time.
-
Zig Native Shell — The generated shell contains four core components: the
Appobject (describes your application), theRuntime(event loop + window management),WebViewSource(what to load — inline HTML, URL, or local assets), and the bridge handler for JS↔Zig communication. -
Web Frontend — Your framework of choice runs inside a WebView. Any modern web app works — you keep your existing dev tools, hot reload, and component libraries.
-
Web Engine Layer — You choose: the System WebView (WKWebView on macOS, WebKitGTK on Linux) for minimal binary size, or Chromium via CEF for consistent rendering across platforms.
-
Native Bridge —
window.zero.invoke()is the JavaScript-to-Zig bridge. Every call is size-limited, origin-checked, permission-checked, and routed only to registered handlers. The bridge gives your web UI access to native platform APIs.
🚀 Step-by-Step Setup
Step 1: Install the CLI
npm install -g zero-native
Verify the installation:
zero-native --help
You should see the CLI help output with init, build, and run commands.
Step 2: Create a New Project
zero-native init my-desktop-app --frontend svelte
cd my-desktop-app
This generates:
app.zon— App manifestbuild.zig— Zig build configurationsrc/— Zig source files (main.zig, app.zig)frontend/— Your Svelte project scaffold
Step 3: Configure app.zon
Open app.zon and customize it:
.{
.id = "com.example.my-desktop-app",
.name = "my-desktop-app",
.display_name = "My Desktop App",
.version = "0.1.0",
.web_engine = "system",
.permissions = .{ "window" },
.capabilities = .{ "webview", "js_bridge" },
.security = .{
.navigation = .{
.allowed_origins = .{ "zero://app", "http://127.0.0.1:5173" },
},
},
.windows = .{
.{ .label = "main", .title = "My App", .width = 960, .height = 640 },
},
}
Key fields explained:
web_engine—"system"for the platform WebView,"chromium"for bundled Chromium/CEFpermissions— What the app can do ("window","fs","clipboard")capabilities— Feature flags ("webview","js_bridge","automation")security.navigation.allowed_origins— Which URLs the WebView can navigate to
Step 4: Add a Native Bridge Call
In your Svelte frontend (frontend/src/App.svelte):
<script>
import { onMount } from "svelte";
let bridgeStatus = $state("checking...");
let message = $state("");
onMount(async () => {
if (window.zero) {
bridgeStatus = "connected";
const result = await window.zero.invoke("hello", { name: "World" });
message = result;
} else {
bridgeStatus = "not available";
}
});
</script>
<main>
<h1>Zero-Native Desktop App</h1>
<p>Bridge: {bridgeStatus}</p>
<p>Response: {message}</p>
</main>
Step 5: Build and Run
zig build run
The first build installs frontend dependencies, compiles the Zig shell, and opens a desktop window rendering your Svelte UI. The frontend dev server runs at http://127.0.0.1:5173 with hot reload — any change you make to the Svelte code refreshes immediately in the native window.
Step 6: Production Build
For a distributable package:
zig build package -- -Dpackage-target=macos
This produces a standalone .app bundle (or .exe for Windows, AppImage for Linux) in zig-out/. The binary size with the system WebView is under 1MB.
🐳 Docker-Based Development
You can also develop zero-native apps in a containerized environment:
# docker-compose.yml
version: "3.8"
services:
dev:
image: ziglang/zig:0.14.0
volumes:
- .:/app
working_dir: /app
ports:
- "5173:5173"
environment:
- DISPLAY=:99
command: >
sh -c "zig build run"
Note: For GUI rendering inside Docker, you'll need an X11 socket or a VNC server. The system WebView requires a display server — for headless testing, use the Chromium/CEF backend with xvfb-run.
🔧 Config Reference
| Setting | Options | Description |
|---|---|---|
web_engine |
system, chromium |
Web rendering backend |
permissions |
window, fs, clipboard, notifications |
Native API access |
capabilities |
webview, js_bridge, automation |
Feature toggles |
web_engine.system |
macOS: WKWebView, Linux: WebKitGTK, Windows: Edge WebView2 | Platform default |
✅ Verification Checklist
- CLI installed:
zero-native --helpshows commands - New project created:
zero-native init my_app --frontend svelte app.zoncontains valid app metadata- Frontend dev server starts:
http://127.0.0.1:5173/ - Native window opens with your UI rendered
- Bridge works:
window.zero.invoke()returns a response - Production package builds without errors
- Binary size is under 2MB (system WebView)
🌟 Alternatives
Zero-native is not the only option for building native apps with web tech. Here's how it compares:
- Electron / Tauri — Electron bundles Chromium (heavy, ~200MB); Tauri uses the system WebView and a Rust backend. Zero-native is like Tauri in concept but uses Zig instead of Rust, offering even smaller binaries and faster compile times.
- Neutralino.js — Uses the system WebView with a Node.js backend. Zero-native replaces Node.js with Zig, giving you a native compilation target and no Node.js dependency in production.
- Flutter (Desktop) — Dart-based, compiles to native code but requires learning Flutter's widget system. Zero-native keeps your existing web frontend skills intact.
Zero-native stands out when you want maximum binary efficiency, already know web frontend frameworks, and prefer Zig's simplicity over Rust's complexity.
📚 Resources
- GitHub: github.com/vercel-labs/zero-native
- Website: zero-native.dev
- Quick Start: zero-native.dev/quick-start
- Documentation: zero-native.dev/docs
- Examples: Next.js, React, Svelte, Vue — iOS and Android mobile embedding examples included
- License: Apache 2.0