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.
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)});}
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)});
}
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.
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"));}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"));
}
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.
.{.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"},}.{
.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" },
}
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
'{' is never closed | A 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 literal | A 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 parsed | The 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 parsed | The 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 statement | The 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.