Project structure

A Verve application is the framework tree — verve-cli new copies the framework source into your project and your code slots into designated directories. There is no package-manager indirection: you can read (and step-debug) every layer you run on.

myapp/
├── build.zig            framework build: server, wasm client, codegen
├── build.zig.zon        package identity (name, fingerprint, paths)
├── src/
│   ├── app/             ← YOUR application
│   │   ├── api.zig      module root: exports routes, Actions, islands
│   │   ├── routes.zig   route table (Route.init / Route.layout)
│   │   ├── components.zig page components (page shell, 404, error)
│   │   └── islands.zig  island registry (Props contracts)
│   ├── client/
│   │   ├── islands/     ← YOUR island implementations (one .zig per island)
│   │   └── ...          wasm runtime (framework)
│   ├── core/            reactive core, router, renderer, viz/anim/gl
│   ├── server/          HTTP server, api handler, push channels
│   ├── desktop/         native window hosts (macOS/Linux/Windows)
│   └── bridge/verve.js  ~minimal JS glue the client wasm talks through
├── tools/               build-time codegen (island manifest, server fns)
└── public/              static assets (opt-in via -Dpublic-dir=public)

The four app files

src/app/api.zig — module root

The build wires this file as the app module. It re-exports the other three and declares Actions — every public function in that struct becomes a typed /api/<fn> endpoint:

pub const components = @import("components.zig");
pub const islands = @import("islands.zig");
pub const routes = @import("routes.zig").routes;

pub const Actions = struct {
    pub fn getCount(_: struct {}) !i32 { ... }
};

src/app/routes.zig — route table

pub const routes: []const verve.Route = &.{
    verve.Route.init("/", renderHome),
    verve.Route.init("/work/:slug", renderWork),
    verve.Route.layout("/app", renderShell, &.{
        verve.Route.init("/dashboard", renderDash),
    }),
    verve.Route.init("/admin", renderAdmin).protect(adminGuard),
};

src/app/components.zig — page components

Functions from *verve.Context to *verve.Node. The server calls three by name: page (shell), notFound, and errorPage.

src/app/islands.zig — island registry

Each pub const <Name> = struct { ... } declares an island the build discovers at configure time. The matching implementation lives at src/client/islands/<Name>.zig and compiles to its own island_<name>.wasm chunk. See Islands.

Build pipeline

zig build runs several phases around your code:

  1. Island discovery — parses islands.zig, generates the client manifest mapping island names to wasm chunk URLs.
  2. Server-fn codegen — walks Actions, generates /api/<fn> dispatch plus typed client-side stubs (app_client.<fn>_call).
  3. WASM compileclient.wasm (main runtime) and one chunk per island, wasm32-freestanding, ReleaseSmall.
  4. Asset embedding-Dpublic-dir files are embedded with content-type detection and hash-busted URLs (ctx.assetHref).
  5. Server compile — native binary with everything baked in. One file deploys the whole site.

Next: The CLI.