Creating a project: zig init

for Zig 0.15.2 Buy

Creating a project: zig init

To create the structure of a project, also called a package, we create a directory, move into it, and simply type zig init:


zig init 


Or, if we want a more minimal version of the configuration:


zig init  --minimal 


Zig will create the project structure. If we use the non-minimal version, we will see that the following files have been generated. You can use the tree command if you are in the shell to view the files recursively:

$ tree

├── build.zig

├── build.zig.zon

└── src

    ├── main.zig

    └── root.zig

build.zig.zon

All these generated files are Zig files, except build.zig.zon. If you open it, it will surely remind you of JSON files (JavaScript Object Notation), but it is actually a similar concept: ZON (Zig Object Notation) - it is Zig’s own notation. It consists of declarative syntax inspired by structs, and if you open it, you will notice that it has a lot in common with them:

build.zig.zon

.{

    .name = .package_name,

   // This is a [Semantic Version](https://semver.org/).

   .version = "0.0.0",

   // Together with the name, it represents

   // a globally unique identifier for the package.

   .fingerprint = 0x,

   // Changing this has security and trust implications.

   // Indicates the minimum Zig version that this package

   // considers supported.

   .minimum_zig_version = "0.15.2",

   // This field is optional.

   // Each dependency must provide a `url.`

   // and a `hash`, or alternatively a `path`.

   // `zig build --fetch` can be used to

   // download all dependencies

   // of a package, recursively.

   // Once downloaded, `zig build` no longer

   // needs an internet connection.

   .dependencies = .{

     // dependencies

   },

  // Specifies the set of files and directories

  // included in this package.

  // Only the files and folders listed here are included

  // in the `hash` that is calculated for the package.

  .paths = .{

    "build.zig",

    "build.zig.zon",

    "src",

    // For example...

    //"LICENSE",

    //"README.md",

  },

}

These files generated by zig init can be created by hand, as long as we follow certain format guidelines.

build.zig.zon contains a structure where we declare the package metadata. These data are read by Zig as typed data at compile time.

The first field is the package name, and it has the peculiarity of having a . (dot) before the name. zig init creates the package name from the name of the directory we previously created for the package. If you do it by hand, remember to replace spaces with _ (underscores). You should also avoid non-alphanumeric characters in the name, and it must not contain accents.

In the .dependencies field, you place the external dependencies of the project, if any exist, and if you do not use any, you can leave it empty. You will probably end up using some dependency anyway.

In the .paths field, you list all the files and directories that we want to include in the package.

If you had not seen Zig structs before, you might think - What a strange kind of JSON is this, and why did they make it like this? And that is exactly what happens to many people who, unlike you, start with the Zig Build System instead of learning the language first.

src

As for the generated source code, there are two files:

This means that if our executable does not include a library part that other packages can use, we can remove root.zig, and if we are making a library that will not have its own executable, we can remove main.zig. Basically, root.zig acts as a hub to expose library-style functionality to other packages.

build.zig

The build.zig file contains the instructions to compile the project. Zig does not execute this file directly when we run zig build: it uses it to create a graph (a data structure) that is used to define the compilation characteristics and steps.

For this very reason, the most important part of this file is the build function:

fn build

 // need this function in order to build the package

 pub fn build(b: *std.Build) void {

This function does not compile, but instead mutates the object passed as a parameter. That parameter b is the builder - a special struct with methods to configure the compilation step by step.

Inside this build function, if we created the project using the non-minimal version, we will see several commands - calls to the builder methods.

target: platform we compile for

The target is defined: the platform for which the executable is compiled. By default, it is compiled for your current machine.

  const target = b.standardTargetOptions(.{});

I, for example, use Linux, but if I wanted to compile an executable for Windows, I could write the following:

  

  const target = b.standardTargetOptions(.{ 

    .default_target = .{

        .cpu_arch = .x86_64,

        .os_tag = .windows,

    } 

  });

optimize: optimization level

Here, we define the optimization level options. By default, that level is Debug.

  const optimize = b.standardOptimizeOption(.{});

If we want, for example, to create a ReleaseSmall, we write:

  const optimize = b.standardOptimizeOption(.{

        .preferred_optimize_mode = .ReleaseSmall,

    });

the executable

We define the executable to be created using the previous specifications.

  const exe = b.addExecutable(.{

        .name = "package_name",

        .root_module = b.createModule(.{

          .root_source_file = b.path("src/main.zig"),

          .target = target,

          .optimize = optimize,

        }),

    });

We declare that the exe will be installed. By default, the zig-out subdirectory will be used, but we can pass --prefix or -p as an argument to use another path.

  b.installArtifact(exe);

Build steps

Zig Build System works step by step. Each of those steps represents a task. By default, zig init has created a step called run_step. This step compiles and runs:

  const run_step = b.step("run", "Run the app");

  const run_cmd = b.addRunArtifact(exe);

  run_step.dependOn(&run_cmd.step);

  run_cmd.step.dependOn(b.getInstallStep());

When you run zig build run, Zig follows the dependency chain: it installs that executable and runs it.

Tests

  const exe_tests = b.addTest(.{

    .root_module = exe.root_module,

  });

  const run_exe_tests = b.addRunArtifact(exe_tests);

  const test_step = b.step("test", "Run tests");

  test_step.dependOn(&run_exe_tests.step);

This looks for and executes the test blocks in the code.

Compiling programs in Zig
Example of using a library: argonaut
© 2025 - 2026 Zen of Zig