# How to Package Native SDK Apps for Distribution on macOS, Linux, and Windows

> Learn to package Native SDK apps for macOS, Linux, and Windows distribution using the native CLI. Distribute your applications easily across multiple platforms.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**TLDR:** Native SDK apps are packaged by the `native` CLI through `zig build package -Dpackage-target=<os>`, which assembles a ReleaseFast binary, `app.zon` manifest, and icon assets into a platform-specific bundle under `zig-out/package/`.

The vercel-labs/native repository provides a Zig-based toolkit for building cross-platform desktop applications with the Native SDK. Packaging these applications for end users is handled by the `native` CLI, which orchestrates the `zig build package` command to produce platform-specific distributables. The workflow is implemented primarily in `src/tooling/templates.zig` and `src/tooling/package.zig`, where build options are parsed, the executable is compiled, and the final archive is assembled.

## Packaging Workflow Overview

The packaging pipeline runs in three phases defined in `src/tooling/templates.zig` and `src/tooling/package.zig`.

### Select a Package Target

The CLI injects a `PackageTarget` option into the build graph. In `src/tooling/templates.zig`, the `package_target` option is parsed around line 1416 and mapped to a target tag near line 1536.

### Build the Executable

`zig build package` first compiles the app using the standard `native build` flow. By default, it emits a **ReleaseFast** binary to `zig-out/bin/<app>`, then passes that binary as a file argument to the packaging step. This behavior is implemented in `src/tooling/templates.zig` at lines 1554–1557.

### Generate the Package Artifact

The packaging step collects the binary, the app icon, and the `app.zon` manifest into a folder under `zig-out/package/`. The directory name follows the pattern:

```

zig-out/package/<app>-0.1.0-<target>-<opt>-<suffix>

```

The final suffix is appended by the `packageSuffix` helper function defined at line 1917 of `src/tooling/templates.zig`. The resulting folder is then converted into a DMG for macOS, an AppImage for Linux, or an MSI for Windows.

## Step-by-Step Packaging Guide

You can generate distributable artifacts for each supported operating system using the same underlying command with different target flags.

| Step | Command | Description |
|------|---------|-------------|
| 1 | `native init my-app` | Scaffold a new project with `app.zon`, source files, and `assets/icon.png`. |
| 2 | `zig build -Doptimize=ReleaseFast` | Compile the app for the host platform. Output goes to `zig-out/bin/`. |
| 3 | `zig build package -Dpackage-target=macos` | Generate `zig-out/package/my-app-0.1.0-macos-ReleaseFast.dmg` or a `.app` bundle. |
| 4 | `zig build package -Dpackage-target=linux` | Generate `zig-out/package/my-app-0.1.0-linux-ReleaseFast.AppImage`. |
| 5 | `zig build package -Dpackage-target=windows` | Generate `zig-out/package/my-app-0.1.0-windows-ReleaseFast.msi`. |

All three packaging commands share the same implementation in `src/tooling/templates.zig`; only the `package_target` flag changes the platform-specific logic.

### Important Build Flags

- `-Dpackage-target=<os>` — Sets the distribution platform to `macos`, `linux`, or `windows`.
- `-Doptimize=<mode>` — Controls the build mode; `ReleaseFast` is the default when packaging.
- `--web-engine <engine>` — (Optional) Chooses the WebView engine for web-based frontends.
- `--cef-dir <path>` — (Optional) Points to a pre-downloaded CEF binary for macOS or Windows.

## Packaging Internals in `src/tooling/templates.zig`

The CLI constructs the packaging step by invoking a system command wired directly into the Zig build graph. As implemented in `src/tooling/templates.zig` (lines 1554–1567), the build script configures the command like this:

```zig
const package = b.addSystemCommand(&.{
    "package",
    @tagName(package_target),
    package_optimize_name,
    b.fmt("zig-out/package/{s}-0.1.0-{s}-{s}{s}",
          .{ app_exe_name, @tagName(package_target), package_optimize_name,
             packageSuffix(package_target) })
});
package.setEnvironmentVariable("NATIVE_SDK_PATH", b.pathFromRoot(native_sdk_path));
package.addFileArg(package_exe.getEmittedBin());
package.addArgs(&.{ "--web-engine", @tagName(web_engine), "--cef-dir", cef_dir });

```

This snippet demonstrates how the binary emitted by `package_exe.getEmittedBin()` is injected into the packaging pipeline alongside metadata flags.

## Required `app.zon` Manifest

Every packaged application must include a valid `app.zon` manifest that declares its identity, supported platforms, and security policy. A minimal example is defined in `src/tooling/templates.zig` (around lines 2450–2470):

```json
{
  "id": "dev.native_sdk.my-app",
  "name": "my-app",
  "display_name": "My App",
  "description": "A counter that lives in one native window.",
  "version": "0.1.0",
  "icons": ["assets/icon.png"],
  "platforms": ["macos", "linux", "windows"],
  "permissions": ["view", "command"],
  "capabilities": ["native_views", "gpu_surfaces"],
  "shell": {
    "windows": [{
      "label": "main",
      "title": "My App",
      "width": 480,
      "height": 320,
      "restore_state": false,
      "views": [{ "label": "main-canvas", "kind": "gpu_surface", "fill": true }]
    }]
  },
  "security": { "navigation": { "allowed_origins": ["zero://app", "zero://inline"] } },
  "web_engine": "system",
  "cef": { "dir": "third_party/cef/<os>", "auto_install": false }
}

```

The `platforms` array must include the target OS, and the `icons` entry is required because the CLI automatically converts `assets/icon.png` into the appropriate platform-specific format (`.icns`, `.ico`, etc.).

## Customizing the Package Build

For advanced use cases, you can modify the low-level packaging behavior in `src/tooling/package.zig`, which serves as the entry point for the `package` command. If you need to embed additional resources, pass extra arguments after `package_exe` is added:

```zig
package.addArgs(&.{ "--extra-file", "extra/config.json" });

```

The `packageSuffix` helper at line 1917 of `src/tooling/templates.zig` determines the final archive name, while the archive creation logic itself resides in `src/tooling/package.zig`.

## Debugging a Package Build

To inspect the exact commands executed during packaging, enable verbose logging:

```bash
zig build package -Dpackage-target=windows -Dverbose=true

```

With verbose mode enabled, the build system prints the generated `package` command and surfaces any errors from the underlying `native_sdk.addApp` call.

## Summary

- Use `zig build package -Dpackage-target=<os>` to create release-ready bundles for macOS, Linux, and Windows.
- The packaging logic is centralized in `src/tooling/templates.zig`, with platform-specific assembly handled by `src/tooling/package.zig`.
- Ensure your `app.zon` manifest lists the target platform and includes an icon asset for automatic conversion.
- Packaged artifacts are written to `zig-out/package/` as DMGs for macOS, AppImages for Linux, and MSIs for Windows.

## Frequently Asked Questions

### What command do I use to package a Native SDK app for macOS?

Run `zig build package -Dpackage-target=macos` from your project root. According to the vercel-labs/native source code, this invokes the packaging step in `src/tooling/templates.zig` and outputs a `.dmg` or `.app` bundle under `zig-out/package/` that includes the ReleaseFast binary and converted icon assets.

### Where does the Native SDK store packaged binaries?

The build system emits the compiled binary to `zig-out/bin/<app>` and then assembles the final distributable folder at `zig-out/package/<app>-0.1.0-<target>-<opt>-<suffix>`. The exact suffix is generated by the `packageSuffix` function in `src/tooling/templates.zig` at line 1917.

### Can I bundle additional files with my Native SDK app?

Yes. You can customize the packaging step by editing `src/tooling/package.zig` or passing extra arguments in your build script, such as `package.addArgs(&.{ "--extra-file", "extra/config.json" });`. The packaging command defined in `src/tooling/templates.zig` accepts these arguments and includes them in the platform-specific archive.

### Does the Native SDK automatically convert my app icon for each platform?

Yes. As long as your `app.zon` manifest references an `assets/icon.png` file, the `native` CLI handles conversion to the required platform formats—`.icns` for macOS, `.ico` for Windows, and PNG/XPM assets for Linux—during the `zig build package` step.