Skip to main content
A gavAI page is a JSON document, not compiled code. The document is a tree of typed blocks — NavShell at the root, then Hero, Form, Table, and the rest nested inside — and the runtime turns that tree into a rendered page on every request. By the end of this page you will know what a block is, why pages are stored as documents, which 10 blocks ship in the platform, and how the document is shaped.

A page, in 15 lines

Here is a page document that renders a navigation shell with a hero banner, a feature grid, and a footer:
page.json
{
  "version": "v0.4",
  "metadata": { "slug": "landing", "title": "Acme Co." },
  "schema": { },
  "tree": {
    "block": "NavShell",
    "props": {},
    "children": [
      { "block": "Hero",         "props": { "headline": "Order management for plumbers." } },
      { "block": "FeaturesGrid", "props": { "columns": 3 } },
      { "block": "Footer",       "props": {} }
    ]
  }
}
That is the whole page. There is no JSX, no React component to compile, no deploy artifact — just a JSON value that the runtime knows how to render. You can GET this document, edit it locally, and PATCH it back.

Why store pages as documents

Storing the page as a document — rather than as code — is the load-bearing decision in the platform. Four properties follow from it. Portable. A page is a value, so it can move around. You can export a workspace’s pages as JSON, copy them into another workspace, and have a working app — assuming the destination workspace has providers wired for the same capabilities (more on that in Capabilities). Versionable. Every change to a page document creates a new version. Rolling back is a single API call. Diffing two versions is a JSON diff, which the runtime and the Builder both understand. AI-readable. A page is a tree of typed nodes that a language model can read and patch reliably. The Builder MCP exposes get_block_schema so an AI agent can introspect any block type at runtime, compute a patch, and apply it — the same operations the Builder UI performs. Multi-renderer. The same document drives server-side rendering on first paint, WebAssembly hydration for interactivity, and static HTML export. You write one document; the platform handles the rendering target.

The mental model: blocks as a typed tree

Think of a page as a typed file system — except instead of folders and files, the nodes are blocks. Each block has a block type (the name), a props object (its configuration), and an optional children array (more blocks nested inside). The analogy breaks down because folders don’t render themselves and files don’t have schemas. Drop the metaphor once the shape clicks. Two rules govern the tree:
  • Type-checked at write time. Every block has a Zod-validated props schema. The runtime rejects a page document that contains a block whose props don’t match — a Hero missing a headline never reaches a rendered page.
  • Bindings are by name, not by reference. A Form block binds to a schema table by name ("schema_table": "leads"); a Table block reads records from a table by name. The binding is resolved at runtime against the workspace’s schema and data.

The 10 block types

Every workspace ships with these. Each one has a typed props schema; the linked reference page is the source of truth for what props it accepts.

The page document, in detail

Every page document has four top-level keys. The example at the top of this page showed them in use; here is what each one carries.

How a page renders

When a request lands at acme.gavai.app/landing, the runtime walks the tree top-down: The first paint is server-rendered HTML. The runtime then ships a WebAssembly bundle that hydrates the page for interactivity — Form submissions, Table filters, KanbanBoard drag-drop. The page document is the input to both stages; you do not write two versions.

When to add a block vs. compose existing ones

Most apps fit inside the 10 built-in blocks. A landing page is NavShellHeroFeaturesGridCtaFooter. A customer portal is NavShellTableDetailPanel. A signup flow is NavShellHeroForm. The 10 cover the common surfaces. If your page wants a shape none of them produce — an embedded video player, a custom chart — that is a sign you need a custom block, which is a separate (and rarer) extension path. Most page changes are prop changes on existing blocks, not new block types.