5.7k

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 macos

native 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:

OptionValuesDefaultDescription
-Dplatformauto, null, macos, linuxautoTarget platform
-Dweb-enginesystem, chromiumapp.zonTemporary WebView engine override; Chromium is currently macOS-only
-Dcef-dirpath--Temporary CEF distribution directory override
-Dtraceoff, events, runtime, alleventsTrace output level
-Ddebug-overlaytrue, falsefalseEnable debug overlay in WebView
-Dautomationtrue, falsefalseEnable automation server
-Djs-bridgetrue, falsefalseEnable 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 },
}
FieldUsed for
idmacOS bundle identifier, Linux desktop file, log paths
display_nameMenu bar name, window title fallback
versionInfo.plist version, package metadata
iconsOne square source image; every platform's icon artifacts generate from it (see App icons)
file_associationsDocument type metadata for macOS, Linux, and Windows registration artifacts
url_schemesCustom protocol metadata for macOS, Linux, and Windows registration artifacts
platformsWhich 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 executable
  • Contents/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 from app.zon
  • Contents/Resources/assets/ -- the app's asset tree, mirrored at its app-relative path so runtime asset paths resolve inside the bundle
  • Contents/Resources/dist/ -- frontend assets (if configured; replaces the assets/ 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 executable
  • share/applications/<name>.desktop -- desktop entry file
  • share/icons/hicolor/.../<name>.png -- icons at standard sizes
  • share/mime/packages/<name>.xml -- shared MIME metadata when file associations are configured
native package --target linux --manifest app.zon --binary zig-out/bin/MyApp

Configured 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.exe

Windows 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-assets

This 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,
}
FieldDescription
distPath to the built frontend output
entryHTML entry point within dist
spa_fallbackServe 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/cef

This 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 macos

Set .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=macos

This 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-icon

This 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.zon

doctor 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

TargetStatus
macosFull support: .app bundle, signing, notarization, DMG
linuxDesktop entry, icon install, binary packaging
windowsEarly support: directory-based packaging
iosExperimental: complete generated Xcode project around the toolkit UIKit host; archive-ready with zero edits (code signing stays manual)
androidExperimental: 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)