Packaging
The Native SDK provides tooling to bundle your app into distributable packages for macOS, Linux, and Windows. A native-rendered app packages as a single binary plus icons, metadata, and whatever lives in your assets/ directory — no browser runtime. On macOS the asset tree is mirrored into the bundle at its app-relative path (Contents/Resources/assets/), and the packaged app resolves relative asset paths (audio files, bundled fonts) against Contents/Resources, so a path like assets/music/track.mp3 names the same file in a dev run and in the installed app. Keep large optional data out of assets/ when you package, or it ships. The frontend-asset and CEF sections below apply only to apps that embed web content; Chromium packaging is currently supported for macOS and includes the CEF runtime when .web_engine = "chromium" and the matching CEF layout is installed.
Quick start
Build and package in two steps:
native build
native package --target macosnative package picks up the manifest at app.zon and the binary at zig-out/bin/<name> automatically; pass --manifest, --binary, or the other flags below for more control. Zero-config apps package without ejecting: the two commands above are the complete path from source to distributable, and native eject exists only for apps that want to own their build files. Apps that do own their build (ejected or scaffolded with --full) also get a zig build package step that wires the same thing into the build graph.
Build options
The build system exposes options that control platform, web engine, and build features:
| Option | Values | Default | Description |
|---|---|---|---|
-Dplatform | auto, null, macos, linux | auto | Target platform |
-Dweb-engine | system, chromium | app.zon | Temporary WebView engine override; Chromium is currently macOS-only |
-Dcef-dir | path | -- | Temporary CEF distribution directory override |
-Dtrace | off, events, runtime, all | events | Trace output level |
-Ddebug-overlay | true, false | false | Enable debug overlay in WebView |
-Dautomation | true, false | false | Enable automation server |
-Djs-bridge | true, false | false | Enable JavaScript bridge |
app.zon packaging fields
The manifest drives packaging metadata:
.{
.id = "com.example.myapp",
.name = "myapp",
.display_name = "My App",
.version = "1.0.0",
.icons = .{"assets/icon.png"},
.platforms = .{ "macos", "linux" },
.web_engine = "system",
.cef = .{ .dir = "third_party/cef/macos", .auto_install = false },
}| Field | Used for |
|---|---|
id | macOS bundle identifier, Linux desktop file, log paths |
display_name | Menu bar name, window title fallback |
version | Info.plist version, package metadata |
icons | One square source image; every platform's icon artifacts generate from it (see App icons) |
file_associations | Document type metadata for macOS, Linux, and Windows registration artifacts |
url_schemes | Custom protocol metadata for macOS, Linux, and Windows registration artifacts |
platforms | Which platform packages to generate |
App icons
Drop one square image in your project — assets/icon.png (1:1, ideally 1024x1024) or assets/icon.svg — list it in .icons, and packaging generates what each platform needs: a complete .icns for macOS (with the platform's rounded-rectangle icon shape and margins applied automatically, so full-bleed artwork looks native in the Dock), a multi-size .ico for Windows, and hicolor PNG size sets for Linux, plus asset-catalog and launcher-mipmap images for the mobile host skeletons. Artwork that already has transparent corners is treated as pre-shaped and ships unmasked. For art-directed control, a prebuilt .icns (macOS) or .ico (Windows) in .icons always wins untouched. Everything is generated by the SDK's own rasterizer and encoders — no external tools. native validate checks the source up front (square, decodable, large enough) with the same messages packaging prints.
macOS
App bundle
native package --target macos creates a .app bundle with:
Contents/MacOS/<binary>-- the compiled executableContents/Resources/AppIcon.icns-- the app icon, generated from your icon source (or your prebuilt.icns, copied untouched under its own name)Contents/Info.plist-- generated fromapp.zonContents/Resources/assets/-- the app's asset tree, mirrored at its app-relative path so runtime asset paths resolve inside the bundleContents/Resources/dist/-- frontend assets (if configured; replaces theassets/mirror)
macOS app bundles declare LSMinimumSystemVersion as 11.0. File associations and URL schemes are emitted as CFBundleDocumentTypes and CFBundleURLTypes entries in Contents/Info.plist.
See Code Signing for signing, notarization, and DMG creation.
Linux
Package structure
Linux packaging creates an install tree:
bin/<name>-- the executableshare/applications/<name>.desktop-- desktop entry fileshare/icons/hicolor/.../<name>.png-- icons at standard sizesshare/mime/packages/<name>.xml-- shared MIME metadata when file associations are configured
native package --target linux --manifest app.zon --binary zig-out/bin/MyAppConfigured file associations and URL schemes are added to the desktop file MimeType list. Extension-only associations get generated application/x-... MIME types with glob patterns in the shared MIME package.
Windows
native package --target windows --manifest app.zon --binary zig-out/bin/MyApp.exeWindows packaging is in early development. The packager copies the binary and assets into a distributable directory structure and writes a multi-size app-icon.ico generated from your icon source (or copies a prebuilt .ico from .icons untouched). When file associations or URL schemes are configured, the artifact also includes install/register-file-types.ps1, which registers the package-local executable under the current user's HKCU\Software\Classes registry keys.
Frontend assets
Bundle assets
If your app has a frontend build step, bundle the output:
zig build bundle-assetsThis copies the configured dist directory into the build output. Production packages serve these through zero://app/, so paths like /assets/app.js work without file:// URLs.
Configure in app.zon
.frontend = .{
.dist = "dist",
.entry = "index.html",
.spa_fallback = true,
}| Field | Description |
|---|---|
dist | Path to the built frontend output |
entry | HTML entry point within dist |
spa_fallback | Serve entry for unknown routes (SPA mode) |
CEF bundling
When using the Chromium engine on macOS, bundle CEF alongside the app:
zig build cef-bundle -Dcef-dir=/path/to/cefThis copies the required CEF framework, libraries, and resources into the app bundle. The CEF distribution must match the macOS target architecture.
Use the same CEF version for install, build, package, and CI verification. The usual app-developer flow is:
native cef install --version <pinned-version>
zig build
native package --target macosSet .web_engine = "chromium" and .cef = .{ .dir = "third_party/cef/macos", .auto_install = false } in app.zon for the normal Chromium package path. Use -Dweb-engine, --web-engine, -Dcef-dir, or --cef-dir only when you need a one-off override.
Verify the Chromium macOS package layout locally with:
zig build test-package-cef-layout -Dplatform=macosThis gated check requires a local CEF layout or -Dcef-auto-install=true and verifies that the packaged app contains the CEF framework and resource files.
Icon generation
Regenerate the SDK's default app icon from its vector source (tools/generate_app_icon.zig):
zig build generate-iconThis renders the full icon family (16 through 1024 px with @2x variants) through the SDK's own path rasterizer, assembles assets/icon.icns and assets/icon.ico with the built-in container writers (round-trip-validated, no external tools, any host OS), and also emits assets/icon.png (the 1024 px master) and an assets/icon.svg mirror for design handoff. The default follows the macOS icon grid — a centered 824x824 rounded-rect plate on the 1024 canvas with standard margins — so it sits correctly in the Dock next to system apps. native init scaffolds the same design as assets/icon.png, the one-image source the packaging pipeline generates every platform's icons from.
Validation
Check that your manifest and environment are ready for packaging:
native doctor --manifest app.zon --strict
native validate app.zondoctor checks the host environment, WebView availability, manifest validity, log paths, and optional CEF paths. Add --strict to fail on any warning. See Debugging for details on what native doctor checks.
Platform shortcut commands
In addition to native package --target <platform>, the CLI provides shortcut commands:
native package-windows [--output path] [--binary path]
native package-linux [--output path] [--binary path]
native package-ios [--output path] [--binary path]
native package-android [--output path] [--binary path]Platform targets
| Target | Status |
|---|---|
macos | Full support: .app bundle, signing, notarization, DMG |
linux | Desktop entry, icon install, binary packaging |
windows | Early support: directory-based packaging |
ios | Experimental: complete generated Xcode project around the toolkit UIKit host; archive-ready with zero edits (code signing stays manual) |
android | Experimental: complete generated host project around the toolkit Android host; the debug APK assembles with zero edits directly with the SDK build tools (store signing stays manual) |