Zig’s package management is decentralized and integrated into the build system. Package metadata is defined in build.zig.zon files, which use Zig’s ZON (Zig Object Notation) format.
Package Manifest (build.zig.zon)
The build.zig.zon file describes your package and its dependencies.
Basic Structure
.{
.name = "myproject",
.version = "0.1.0",
.dependencies = .{},
.paths = ."",
}
Complete Example
// The Zig compiler is not intended to be consumed as a package.
// The sole purpose of this manifest file is to test the compiler.
.{
.name = .zig,
.version = "0.0.0",
.dependencies = .{
.standalone_test_cases = .{
.path = "test/standalone",
},
.link_test_cases = .{
.path = "test/link",
},
},
.paths = ."",
.fingerprint = 0xc1ce108124179e16,
}
Package Fields
Required Fields
The package name, used as an identifier:
Or as an identifier literal:
Files and directories to include in the package:
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"lib",
"LICENSE",
"README.md",
},
Optional Fields
.dependencies - External package dependencies
.fingerprint - Cache validation hash (auto-generated)
Dependencies
Dependencies can reference local paths, URLs, or git repositories.
Local Path Dependencies
.dependencies = .{
.my_module = .{
.path = "../my-module",
},
.test_cases = .{
.path = "test/standalone",
},
},
URL Dependencies
.dependencies = .{
.ziglibs = .{
.url = "https://github.com/user/repo/archive/commit-hash.tar.gz",
.hash = "12208d2b8c95e6e5d2e1e5b5a5c5e5f5a5c5e5f5a5c5e5f5a5c5e5f5a5c5e5f5",
},
},
When adding a URL dependency without a hash, run zig build and Zig will report the expected hash that you can add to your manifest.
Git Dependencies
.dependencies = .{
.example = .{
.url = "git+https://github.com/user/repo#branch-or-tag",
.hash = "1220...",
},
},
Using Dependencies in build.zig
Accessing Dependencies
Dependencies are accessed through the dependency() function:
const my_module = b.dependency("my_module", .{
.target = target,
.optimize = optimize,
});
Importing Modules from Dependencies
// Get a module from a dependency
const dep_module = my_module.module("library");
// Add it to your executable
exe.root_module.addImport("mylib", dep_module);
In your source code:
const mylib = @import("mylib");
pub fn main() void {
mylib.doSomething();
}
Passing Options to Dependencies
You can pass build options when loading a dependency:
const my_dep = b.dependency("my_dep", .{
.target = target,
.optimize = optimize,
.enable_feature = true,
.max_connections = 100,
});
The dependency’s build.zig can access these via b.option().
Creating Modules for Export
Public Modules
Create modules that other packages can import:
// In your build.zig
const mylib = b.addModule("mylib", .{
.root_source_file = b.path("src/lib.zig"),
});
Other packages can then access it:
const dep = b.dependency("your_package", .{...});
const mylib = dep.module("mylib");
exe.root_module.addImport("mylib", mylib);
Private Modules
Create modules for internal use only:
const internal_mod = b.createModule(.{
.root_source_file = b.path("src/internal.zig"),
});
Dependency Resolution
Zig’s package manager uses content-addressable storage:
Dependencies are stored in the global cache:
~/.cache/zig/p/ (Linux/macOS)
%LOCALAPPDATA%\zig\p\ (Windows)
The .hash field uses the multihash format:
First 4 hex chars: Hash algorithm ID (1220 = SHA-256)
Remaining chars: Hash value
The .fingerprint field caches the hash of your package contents. Zig regenerates it when needed.
Package Organization Best Practices
Recommended Structure
myproject/
├── build.zig
├── build.zig.zon
├── src/
│ ├── main.zig
│ └── lib.zig
├── test/
│ └── tests.zig
├── LICENSE
└── README.md
Exporting Multiple Modules
// Export different modules for different use cases
const core = b.addModule("core", .{
.root_source_file = b.path("src/core.zig"),
});
const utils = b.addModule("utils", .{
.root_source_file = b.path("src/utils.zig"),
});
const extras = b.addModule("extras", .{
.root_source_file = b.path("src/extras.zig"),
});
extras.addImport("core", core);
Consumers can import selectively:
const dep = b.dependency("mylib", .{});
exe.root_module.addImport("core", dep.module("core"));
exe.root_module.addImport("utils", dep.module("utils"));
Working with Dependencies
Available Dependencies
Check available dependencies at build time:
const AvailableDeps = []const struct { []const u8, []const u8 };
The available_deps field maps dependency names to their package hashes.
Dependency Caching
lib/std/Build.zig:152-156
const InitializedDepKey = struct {
build_root_string: []const u8,
user_input_options: UserInputOptionsMap,
};
Dependencies are cached based on:
- Build root path
- User input options (target, optimize, custom options)
This ensures the same dependency with different options is built separately.
Transitive Dependencies
Zig automatically handles transitive dependencies. If package A depends on package B, and package B depends on package C, all three will be resolved and cached correctly.
Lazy Dependencies
needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty,
Dependencies are only fetched and built when actually used in the build process.
Example: Multi-Package Project
Root Package (build.zig.zon)
.{
.name = "myapp",
.version = "1.0.0",
.dependencies = .{
.network = .{
.path = "../network-lib",
},
.utils = .{
.url = "https://example.com/utils-v2.tar.gz",
.hash = "1220abcd...",
},
},
.paths = ."",
}
Root Package (build.zig)
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Load dependencies
const network = b.dependency("network", .{
.target = target,
.optimize = optimize,
});
const utils = b.dependency("utils", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "myapp",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
// Import dependency modules
exe.root_module.addImport("network", network.module("network"));
exe.root_module.addImport("utils", utils.module("utils"));
b.installArtifact(exe);
}
Troubleshooting
Hash Mismatches
If you see a hash mismatch error:
- Remove the
.hash field from build.zig.zon
- Run
zig build
- Copy the expected hash from the error message
- Add it to your manifest
Updating Dependencies
To update a dependency:
- Update the URL or path in
build.zig.zon
- Remove or update the hash
- Run
zig build to get the new hash
- Update the manifest with the new hash
Cache Issues
To clear the dependency cache:
Then run zig build to re-download dependencies.