Zig Code Formatter (zig fmt)

Paste Zig code and get the canonical layout from zig fmt, compiled to WebAssembly. Like gofmt, it has one style and no settings to argue over.

Input

Settings

History

Load from URL

One canonical style

Zig ships its formatter inside the compiler as zig fmt, and the Zig community treats its output as the only style. This page runs that formatter as WebAssembly, so you can tidy a snippet from a GitHub issue, the Zig forum or a blog post without installing a particular Zig version. Zig still changes between releases, and having a formatter that does not care which toolchain is on your machine is convenient. The code is processed inside your browser tab.

There are no options. Indentation is always 4 spaces, so the toolbar’s Indent menu is ignored for Zig. Ctrl/Cmd+Enter runs zig fmt; Ctrl/Cmd+Shift+C copies what it printed. Input containing @import("std") or a pub fn declaration is detected as Zig automatically.

Trailing commas decide the layout

zig fmt has no line length limit. Instead, it lets you choose the shape of each list with a trailing comma:

  • Write .{ 1, 2, 3, 4 } without a comma after the last element and it stays on one line, however long that line gets.
  • Add a trailing comma, as in [_]u8{1,2,3,4,}, and every element is put on its own line with the closing brace below.

The same rule applies to function arguments, parameter lists, struct literals and switch prongs. To split a long call, add a comma after its last argument and format again. To join a list back onto one line, remove the comma.

Other changes are what you would expect: spaces around operators and after colons in types (items: []const i64), one statement per line, blocks indented by four spaces, and runs of blank lines reduced to one. Multiline string literals that start with \\ are re-indented but their contents are untouched. Regions between // zig fmt: off and // zig fmt: on are copied exactly.

build.zig.zon files

The package manifest build.zig.zon is written in ZON, Zig Object Notation, which is a single anonymous struct literal such as .{ .name = .app, .version = "0.1.0" }. Paste the whole manifest and it formats like any Zig expression. The trailing comma rule works the same way, which is why a dependencies block with a comma after each entry is printed one entry per line.

How syntax problems are reported

This build of zig fmt does not return the compiler’s own parse errors. To avoid printing a half-formatted file, the page checks the input first: unbalanced brackets and unterminated strings are reported with their exact position, and if the formatter drops or changes any word of your code, the page refuses the output and marks where it went wrong. Some mistakes still get through unreported because the parser recovers from them. A missing semicolon at the end of a statement is the common one: the line is formatted and left without its semicolon, and you only find out when zig build stops with “expected ‘;’ after statement”. Semantic errors such as unused locals, integer overflow or wrong types are never visible to a formatter.

Examples

Sum function and main

Each statement gets its own line, types get spaces after colons, and the short array literal stays on one line.

Input
const std=@import("std");
pub fn sum(items:[]const i64)i64{var total:i64=0;for(items)|x|{total+=x;}return total;}
pub fn main()!void{const xs=[_]i64{1,2,3,4};
std.debug.print("sum={d}\n",.{sum(&xs)});}
Output
const std = @import("std");
pub fn sum(items: []const i64) i64 {
    var total: i64 = 0;
    for (items) |x| {
        total += x;
    }
    return total;
}
pub fn main() !void {
    const xs = [_]i64{ 1, 2, 3, 4 };
    std.debug.print("sum={d}\n", .{sum(&xs)});
}
Open this example in the tool

Struct with a method and error handling

The struct fields and method are indented inside the struct, and the switch on the error gets one prong per line.

Input
const Order=struct{id:u64,total:f64,
pub fn isLarge(self:Order)bool{return self.total>1000;}};
fn parseQty(s:[]const u8)!u32{return std.fmt.parseInt(u32,s,10) catch |err| switch(err){error.Overflow=>return error.TooBig,else=>return err};}
test "parse quantity"{try std.testing.expectEqual(@as(u32,12),try parseQty("12"));}
Output
const Order = struct {
    id: u64,
    total: f64,
    pub fn isLarge(self: Order) bool {
        return self.total > 1000;
    }
};
fn parseQty(s: []const u8) !u32 {
    return std.fmt.parseInt(u32, s, 10) catch |err| switch (err) {
        error.Overflow => return error.TooBig,
        else => return err,
    };
}
test "parse quantity" {
    try std.testing.expectEqual(@as(u32, 12), try parseQty("12"));
}
Open this example in the tool

build.zig.zon manifest

Trailing commas push the top-level fields and the dependency entry onto separate lines, while the paths list stays on one.

Input
.{.name=.shop_api,.version="0.3.1",.dependencies=.{.httpz=.{.url="https://example.com/httpz/archive/v0.9.tar.gz",.hash="1220c0ffee",},},.paths=.{"build.zig","build.zig.zon","src"},}
Output
.{
    .name = .shop_api,
    .version = "0.3.1",
    .dependencies = .{
        .httpz = .{
            .url = "https://example.com/httpz/archive/v0.9.tar.gz",
            .hash = "1220c0ffee",
        },
    },
    .paths = .{ "build.zig", "build.zig.zon", "src" },
}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
'{' is never closedA function, struct or block opened with { has no matching closing brace before the end of the input.Add the missing } at the end of the block that starts at the reported position.
Expected ')' to close '(', found '}'Brackets are closed in the wrong order, typically a function call missing its closing parenthesis inside a block.Add the missing ) before the brace at the reported position.
Unterminated string literalA double-quoted string has no closing quote on the same line. Zig strings cannot span lines.Close the quote, or use a multiline string literal with \ at the start of each line.
Zig syntax error near here — the code could not be parsedThe formatter could not make sense of the code at this point and its output would have lost words, for example a missing comma between parameters as in fn add(a: i32 b: i32).Look just before the highlighted spot for a missing comma, parenthesis or keyword.
Zig syntax error — the code could not be parsedThe formatter produced no output at all, usually because a declaration is incomplete, as in const x = ;.Complete the declaration or remove it, then format again.
No error here, but zig build says expected ';' after statementThe formatter recovers from a missing semicolon and formats the line without one.Add the semicolon at the end of the statement the compiler points to.

Frequently asked questions

Is this the same as running zig fmt?

Yes. The formatting comes from zig fmt compiled to WebAssembly, so the layout matches the command-line tool for code that parses.

How do I make zig fmt split a long line?

Add a trailing comma after the last argument or element and format again. zig fmt has no line length limit, so the comma is the signal to go multi-line.

Can I change the indentation to 2 spaces or tabs?

No. zig fmt always indents with 4 spaces and has no settings, which is why the toolbar Indent menu has no effect on Zig.

Can I format build.zig.zon?

Yes. Paste the manifest as it is; ZON is a Zig struct literal and formats with the same rules.

Will it catch compile errors?

No. It reports bracket and string problems and some parse failures, but missing semicolons can slip through, and type errors or unused variables are only found by the compiler.

Related tools