webview_app

A desktop application combining a web-based UI with native functionality.

Source Code

Path: examples/webview_app/

const builtin = @import("builtin");
const c = @import("c");
const std = @import("std");
const tk = @import("tokamak");

const App = struct {
    server: tk.Server,
    server_opts: tk.ServerOptions = .{},
    routes: []const tk.Route = &.{
        .get("/*", tk.static.dir("public", .{})),
        .get("/api/hello", hello),
    },

    fn hello() ![]const u8 {
        return "Hello, world!";
    }
};

pub fn main(init: std.process.Init) !void {
    try tk.app.run(init, webviewMain, &.{App});
}

pub fn webviewMain(server: *tk.Server, gpa: std.mem.Allocator) !void {
    const address = server.http.config.address;

    const thread = try server.http.listenInNewThread();
    defer thread.join();

    const w = c.webview_create(if (builtin.mode == .Debug) 1 else 0, null);
    defer _ = c.webview_destroy(w);

    _ = c.webview_set_title(w, "Example");
    _ = c.webview_set_size(w, 800, 500, c.WEBVIEW_HINT_NONE);

    const url = try std.fmt.allocPrintSentinel(gpa, "http://{f}", .{address}, 0);
    defer gpa.free(url);

    _ = c.webview_navigate(w, url);
    _ = c.webview_run(w);
    server.stop();
}

Features Demonstrated

  • Webview integration for desktop apps
  • Static file serving
  • API endpoints for backend logic
  • Server running in background thread
  • C library integration (@cImport)
  • Cross-platform desktop app development

Prerequisites

This example requires the webview library to be installed on your system.

Architecture

The application combines:

  1. Backend Server: Runs in a separate thread, serves static files and API endpoints
  2. Webview Window: Embeds a browser that loads the local server

const App = struct {
    server: tk.Server,
    server_opts: tk.ServerOptions = .{},
    routes: []const tk.Route = &.{
        .get("/*", tk.static.dir("public", .{})),
        .get("/api/hello", hello),
    },

    fn hello() ![]const u8 {
        return "Hello, world!";
    }

How It Works

The main function shows the complete flow:


pub fn main(init: std.process.Init) !void {
    try tk.app.run(init, webviewMain, &.{App});
}

pub fn webviewMain(server: *tk.Server, gpa: std.mem.Allocator) !void {
    const address = server.http.config.address;

    const thread = try server.http.listenInNewThread();
    defer thread.join();

    const w = c.webview_create(if (builtin.mode == .Debug) 1 else 0, null);
    defer _ = c.webview_destroy(w);

    _ = c.webview_set_title(w, "Example");
    _ = c.webview_set_size(w, 800, 500, c.WEBVIEW_HINT_NONE);

    const url = try std.fmt.allocPrintSentinel(gpa, "http://{f}", .{address}, 0);
    defer gpa.free(url);

    _ = c.webview_navigate(w, url);
    _ = c.webview_run(w);
    server.stop();
}

Routes

Routes are defined in the App struct:

    server_opts: tk.ServerOptions = .{},
    routes: []const tk.Route = &.{
        .get("/*", tk.static.dir("public", .{})),
        .get("/api/hello", hello),
  • .get("/*", tk.static.dir("public", .{})) serves all files from the public/ directory
  • .get("/api/hello", hello) is a backend API that the frontend can call

Frontend Integration

Your frontend JavaScript can call the backend API:

fetch('/api/hello')
  .then(response => response.text())
  .then(data => console.log(data));

Development vs Production

The webview can show dev tools in debug mode (see line 36 in the source):

    defer gpa.free(url);

The first argument to webview_create is 1 for dev tools enabled, 0 for disabled.

Running

cd examples/webview_app
zig build run

A desktop window will open showing your web UI.

Use Cases

This pattern is great for:

  • Desktop applications with web UI
  • Tools that need native OS integration
  • Applications requiring file system access
  • Cross-platform GUI apps without heavy frameworks

Architecture Benefits

  • Familiar Technologies: Build UI with HTML/CSS/JavaScript
  • Backend Power: Full Zig capabilities for system operations
  • Small Binary: No Electron overhead
  • Native Performance: Direct system access from Zig backend

Next Steps

  • Add more API endpoints for your application logic
  • Implement file system operations in the backend
  • Use WebSockets for real-time communication
  • Explore the webview library for more platform-specific features