A Zig-first WebUI runtime with typed RPC, deterministic launch policy, native host launch paths, and browser/web fallbacks.
- π§ Comptime RPC surface: declare
pub const rpc_methodsonce and generate JS/TS bridge clients. - π― Deterministic launch policy: explicit, ordered surface selection via
LaunchPolicy. - πͺ Window controls + style API:
WindowStyle,WindowControl, capability probing, close handlers. - π WS-first runtime signaling: reconnecting WebSocket channel for push events and script tasks.
- π Broad browser support: aggressive browser discovery and cross-platform search paths.
- π§ͺ Strong build gates: tests, examples, parity checks, and OS matrix steps in
build.zig. - π§± Pure Zig active graph: no
@cImport, notranslate-c, no active C runtime compilation path.
zig build
zig build test
zig build examples
zig build runRun one example:
zig build run -Dexample=fancy_windowList all build steps/options:
zig build -l
zig build -hFor example runs (zig build run -Drun-mode=...):
webview: native webview firstbrowser: external browser app-windowweb-tab: browser tabweb-url: serve URL only; do not auto-open browser- Ordered combinations are supported, e.g.
webview,browser,web-url
Examples:
zig build run -Dexample=minimal -Drun-mode=webview
zig build run -Dexample=minimal -Drun-mode=browser
zig build run -Dexample=minimal -Drun-mode=web-tab
zig build run -Dexample=minimal -Drun-mode=webview,browser,web-urlbrowser_window: closing the browser window should close backend.browser_window: refreshing should not kill backend (grace + reconnect handling).web_url/ web-tab style workflows: closing tab should not kill backend by default.
- Generic
webviewmode defaults to WebKit2GTK4.1(fallback4.0), not WebKitGTK6. - WebKitGTK
6is explicit opt-in viaAppOptions.linux_webview_target = .webkitgtk_6. - If the selected native runtime is unavailable, launch policy falls back to browser/web-url surfaces.
const std = @import("std");
const webui = @import("webui");
pub const rpc_methods = struct {
pub fn ping() []const u8 { return "pong"; }
pub fn add(a: i64, b: i64) i64 { return a + b; }
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var service = try webui.Service.init(gpa.allocator(), rpc_methods, .{
.app = .{
.launch_policy = .{
.first = .native_webview,
.second = .browser_window,
.third = .web_url,
},
.linux_webview_target = .webview,
},
.window = .{ .title = "WebUI Zig" },
.rpc = .{ .dispatcher_mode = .threaded },
});
defer service.deinit();
try service.showHtml(
"<!doctype html><html><head><meta charset=\"utf-8\"/>" ++
"<script type=\"module\" src=\"/webui_bridge.js\"></script></head>" ++
"<body><button id=\"b\">Ping</button><pre id=\"out\"></pre>" ++
"<script>document.getElementById('b').onclick=async()=>{" ++
"document.getElementById('out').textContent=" ++
"`ping=${await webuiRpc.ping()} add=${await webuiRpc.add(20,22)}`;};</script>" ++
"</body></html>",
);
while (!service.shouldExit()) {
try service.run();
std.Thread.sleep(10 * std.time.ns_per_ms);
}
}AppOptions uses deterministic LaunchPolicy ordering.
BrowserLaunchOptions uses explicit profile_rules:
ProfilePathSpec:default | ephemeral | customProfileRuleTarget:webview | browser_any | browser_kind- Rule order is authoritative (first match wins).
- Browser default profile semantics are empty path (
"") and no forced--user-data-dir. - Webview default profile path uses OS-standard app-data/config locations.
Helpers:
webui.browser_default_profile_pathwebui.profile_base_prefix_hintwebui.resolveProfileBasePrefix(allocator)webui.defaultWebviewProfilePath(allocator)
| Flag | Default | Effect |
|---|---|---|
-Ddynamic=true |
false |
Build shared library (.so/.dylib/.dll) instead of static archive. |
-Denable-tls=true |
false |
Enable HTTPS/WSS runtime defaults (TLS certificate + secure local transport). |
-Denable-webui-log=true |
false |
Enable runtime logging defaults. |
-Dminify-embedded-js=true |
true |
Minify embedded runtime helper JS at build time. |
-Dminify-written-js=true |
false |
Minify written runtime helper JS artifact. |
-Dexample=<name> |
all |
Select which demo zig build run executes. |
-Drun-mode=<tokens> |
webview,browser,web-url |
Example launch order/preset tokens. |
-Dlinux-webview-target=<name> |
webview |
Linux native target for examples: webview (WebKit2GTK 4.1/4.0) or webkitgtk-6. |
-Dtarget=<triple> |
host | Cross-compile target. |
Add as dependency:
zig fetch --save <git-or-tarball-url>build.zig:
const webui_dep = b.dependency("webui", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("webui", webui_dep.module("webui"));zig build test
zig build examples
zig build parity-local
zig build os-matrixUseful additional steps:
zig build dispatcher-stress
zig build bridge
zig build runtime-helpersRun all:
zig build runRun one:
zig build run -Dexample=translucent_rounded -Drun-mode=webview,browser,web-urlLinux explicit WebKitGTK 6 run:
zig build run -Dexample=translucent_rounded -Drun-mode=webview -Dlinux-webview-target=webkitgtk-6Available -Dexample values include:
minimal,call_js_from_zig,call_zig_from_jsbidirectional_rpcserve_folder,vfs,public_network,multi_clientchatgpt_api,custom_web_server,reactframeless,fancy_window,translucent_rounded,text_editorminimal_oop,call_js_oop,call_oop_from_js,serve_folder_oop,vfs_oop
Current strengths:
- Typed RPC + generated bridge tooling
- Deterministic launch behavior and runtime introspection
- WS-first signaling and fallback surface control
- Multi-OS build/test/parity gates
Still tracked for strict upstream behavioral parity:
- Full in-process native host completion on Windows/macOS
- Visual/window parity items requiring manual GUI verification on real desktops
See:
parity/status.jsonDOCUMENTATION.md
App and Service are move-sensitive after window initialization.
- Do not copy/move initialized
App/Servicevalues by value. - Keep them in final storage and pass pointers (
*App,*Service).
In debug-safe builds, move-invariant diagnostics are emitted and fail fast to avoid latent crashes.
DOCUMENTATION.md
See LICENSE.