Skip to content

Start here

Getting started #

This page shows the minimum steps to open a Knots window. For a full application, do the todo app tutorial after this page.

Requirements #

  • Zig. Knots follows the Zig master branch. Use the version in the minimum_zig_version field of the Knots build.zig.zon. At the time of writing, this is 0.17.0-dev.2131+d08989840.
  • Linux only: install the Wayland development packages: wayland-client, wayland-cursor, wayland-protocols, wayland-scanner, pkg-config, and xkbcommon.
  • A GPU driver for the backend that you use. Read GPU backends.

Add Knots to a project #

In your project directory, run this command:

zig fetch --save git+https://github.com/knots-ui/knots.git

The command adds a knots entry to the dependencies in your build.zig.zon.

Connect the build graph #

In build.zig, get the dependency. Then import the knots and ui modules into your executable.

const knots = b.dependency("knots", .{
    .target = target,
    .optimize = optimize,
});

const exe = b.addExecutable(.{
    .name = "app",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
        .imports = &.{
            .{ .name = "knots", .module = knots.module("knots") },
            .{ .name = "ui", .module = knots.module("ui") },
        },
    }),
});
b.installArtifact(exe);

The dependency accepts these build options:

Option Default Effect
gpu_backend Per platform .webgpu or .vulkan. Read GPU backends.
web_threads true Browser builds only. Enables worker threads. Read Compile & distribute.

Draw a first frame #

Put this code in src/main.zig:

const std = @import("std");
const knots = @import("knots");
const ui = @import("ui");

pub fn main(init: std.process.Init) !void {
    var app = try knots.App.init(init.io, init.gpa, .{
        .window = .{ .width = 1280, .height = 720, .title = "My app" },
    });
    defer app.deinit();
    try app.start(frame);
}

fn frame(_: *knots.View, context: *ui.Frame) !void {
    const size = context.input().logical_extent;
    try context.e(.{
        ui.component.Rect{ .key = .src(@src()), .style = &.{
            .width = .fixed(@floatFromInt(size.width)),
            .height = .fixed(@floatFromInt(size.height)),
            .padding = .all(16),
            .background = .bg,
        } },
        .{
            ui.component.Text{ .key = .src(@src()), .content = "Hello from Knots" },
        },
    });
}

The code does these steps:

  1. App.init opens a native window and creates a renderer.
  2. App.start runs the event loop. It calls frame each time the window needs a new frame. It returns when the window closes.
  3. context.e emits a component tree. A component followed by a tuple contains the components in that tuple.
  4. .key = .src(@src()) gives each component a stable identity. Read Keys.

Run it #

zig build run

The first build downloads the dependencies and compiles the shaders. Later builds are faster.

Next step: do the tutorial. It adds state, input, styles, and a browser build.