> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# PageBlocks

> Composable JSON building blocks for gavAI pages — what they are and how they fit together.

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const Def = ({term, type, defaultValue, required, children}) => <>
    <dt className="gv-def-list__term">
      <span className="gv-def-list__name">{term}</span>
      {type && <span className="gv-def-list__type">{type}</span>}
      {required && <span className="gv-def-list__required">required</span>}
      {defaultValue !== undefined && <span className="gv-def-list__default">
          default <code>{defaultValue}</code>
        </span>}
    </dt>
    <dd className="gv-def-list__description">{children}</dd>
  </>;

export const DefList = ({children}) => <dl className="gv-def-list">{children}</dl>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

export const FeatureTable = ({columns, rows, featuredRow, caption, variant}) => <div className={`gv-feature-table ${variant ? `gv-feature-table--${variant}` : ""}`}>
    <table>
      {caption && <caption className="gv-feature-table__caption">{caption}</caption>}
      <thead>
        <tr>
          {columns.map(col => <th key={col.key} scope="col">
              <span className="gv-feature-table__header">{col.label}</span>
            </th>)}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, i) => {
  const rowClass = [i === featuredRow ? "gv-feature-table__row--featured" : "", row.chosen ? "gv-feature-table__row--chosen" : ""].filter(Boolean).join(" ");
  return <tr key={i} className={rowClass}>
              {columns.map(col => {
    const v = row[col.key];
    if (variant === "tradeoff" && Array.isArray(v)) {
      const pill = col.key === "pros" ? "gv-feature-table__pill--pro" : col.key === "cons" ? "gv-feature-table__pill--con" : "";
      return <td key={col.key}>
                      {v.map((item, j) => <span key={j} className={`gv-feature-table__pill ${pill}`}>{item}</span>)}
                    </td>;
    }
    return <td key={col.key}>{v}</td>;
  })}
            </tr>;
})}
      </tbody>
    </table>
  </div>;

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CONCEPT" />

<PageMeta audience="Developers building gavAI pages" readingTime="6 min" level="Beginner" />

A gavAI page is a JSON document describing a tree of blocks. PageBlocks are the ten block types that tree can contain — `NavShell`, `Hero`, `Form`, `Table`, and the rest. By the end of this page you will know what a block is, how blocks compose into a complete page, and where to look up the props for any individual block.

The point of the design: every page is *data*, not code. You can `GET` a page, edit the JSON, and `PATCH` it back — no build step, no deploy. The runtime takes the document and renders it: server-rendered HTML on first load, then a WebAssembly bundle in the browser for interactivity.

## A first example

Here is a complete page document — a marketing landing page with navigation, a hero, a feature grid, and a footer:

```json landing.json theme={null}
{
  "version": "v0.4",
  "metadata": { "slug": "landing", "title": "Acme Co." },
  "schema": {},
  "tree": {
    "block": "NavShell",
    "props": {
      "brand_name": "Acme Co.",
      "variant": "topbar",
      "items": [{ "label": "Home", "href": "/" }, { "label": "Pricing", "href": "/pricing" }]
    },
    "children": [
      { "block": "Hero", "props": { "headline": "Welcome to Acme", "cta_label": "Sign up", "cta_href": "/signup" } },
      { "block": "FeaturesGrid", "props": { "columns": 3, "items": [] } },
      { "block": "Footer", "props": { "copyright": "© 2026 Acme Co.", "groups": [] } }
    ]
  }
}
```

Three things are happening. `version` pins the document schema revision so the runtime knows how to interpret the fields. `metadata` carries the URL slug and HTML title. `tree` is a single root block (`NavShell`) wrapping the page's sections as `children`. Each child names a `block` type, carries its configuration in `props`, and may itself contain further nested blocks.

## The mental model

Think of a block as a **named, typed UI primitive**. The name says what kind of section it is (`Hero`, `Form`). The props are its configuration (`headline`, `cta_href`, `schema_table`). The tree is the page.

```mermaid theme={null}
graph TD
    Doc[Page document]
    Doc --> Tree[tree]
    Tree --> Nav[NavShell]
    Nav --> Hero[Hero]
    Nav --> Grid[FeaturesGrid]
    Nav --> Cta[Cta]
    Nav --> Footer[Footer]
```

Two properties fall out of this shape. Pages are **portable** — the same JSON renders the same page in any workspace running the same runtime version. And pages are **AI-friendly** — a language model can read the tree, propose a JSON Patch, and round-trip an edit reliably, which is why the Builder MCP exposes block schemas as introspection tools.

The analogy has a limit. Blocks are not React components you can write inline — you cannot ship a custom block type by dropping a `.tsx` file in your repo. The block catalogue is closed; new types ship with the runtime. What you customize is the *composition* and the *props*.

## The ten block types

<Columns cols={2}>
  <BrandCard eyebrow="BLOCK" title="NavShell" href="/pageblocks/blocks/nav-shell">
    Outer page chrome — sidebar or topbar navigation. Always the root of the tree.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="Hero" href="/pageblocks/blocks/hero">
    Banner with headline, subhead, and optional CTA. Usually the first section.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="FeaturesGrid" href="/pageblocks/blocks/features-grid">
    Responsive grid of icon-backed cards. The standard product-features section.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="Cta" href="/pageblocks/blocks/cta">
    Single-message conversion band — one headline, one button.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="Footer" href="/pageblocks/blocks/footer">
    Grouped link columns and a copyright line. Usually the last section.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="SectionDivider" href="/pageblocks/blocks/section-divider">
    Theme-aware horizontal rule between content sections.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="Form" href="/pageblocks/blocks/form">
    Inline form bound to a schema table; fires a capability on submit.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="Table" href="/pageblocks/blocks/table">
    Paginated, filterable, sortable record list backed by `runQuery`.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="DetailPanel" href="/pageblocks/blocks/detail-panel">
    Single-record view with optional in-place edit mode.
  </BrandCard>

  <BrandCard eyebrow="BLOCK" title="KanbanBoard" href="/pageblocks/blocks/kanban-board">
    Drag-drop workflow board grouped by a status field.
  </BrandCard>
</Columns>

The first six are **presentational** — pure layout with no data binding. The last four are **data-bound** — they read or write rows in a schema table you declare on the page document.

## Beyond the core ten

The ten blocks above cover the structural skeleton of a page. The runtime ships a broader palette of *section* and *interactive* blocks that you compose as children inside the structural ones. They share the same JSON shape — `block`, `props`, `children` — and the same theming, visibility, and analytics props.

**Marketing sections** are presentational blocks meant for landing-page content above and below the structural shell. The catalogue covers the layouts you reach for when describing a product: an alternating features section, a card-style bento grid, a callout band, a comparison table, an FAQ accordion, a logo cloud, a pricing table, a pull quote, a stats row, a testimonials carousel, and a newsletter sign-up. Each one is a typed block with its own props — you do not stitch them out of raw HTML.

**Interactive blocks** are data-bound surfaces tuned for end-user-facing apps that go beyond a basic list-and-detail view. The catalogue includes a calendar view for date-indexed records, an intake form tuned for multi-step lead capture, and a card-style list view variant for record browsing where the table block would feel heavy.

Both families render through the same pipeline as the core ten: server-side HTML on first load, then a WebAssembly bundle in the browser for interactivity. A page that mixes a `Hero`, a marketing `Pricing` section, and a data-bound `Calendar` view is a single document, not three.

## Common props

Every block accepts these four optional props in addition to its own type-specific ones.

<DefList>
  <Def term="id" type="string">
    Stable client-side identifier for analytics and accessibility hooks.
  </Def>

  <Def term="class_overrides" type="object">
    Per-instance theme overrides applied on top of workspace token values.
  </Def>

  <Def term="visible_if" type="expression string">
    Conditional rendering expression evaluated at runtime. Example: a string referencing the current user role with template syntax.
  </Def>

  <Def term="analytics_event" type="string">
    Event name fired when the block becomes visible or is clicked.
  </Def>
</DefList>

## How blocks pick up the theme

Every visual property a block exposes — color, spacing, type scale, corner radius — resolves through workspace theme tokens, not hard-coded values. Change one token and every block instance picks up the new value on the next render. When you need a one-off tweak on a single block, use `class_overrides` on that instance — it layers on top of the resolved token values and does not affect any other block. See [Theme](/pageblocks/theme) for the full token model.

## Where to go next

<Columns cols={2}>
  <BrandCard eyebrow="MODEL" title="The page document" href="/pageblocks/page-document">
    Top-level shape — version, metadata, schema, tree — and the document lifecycle from draft to published.
  </BrandCard>

  <BrandCard eyebrow="EDITING" title="Editing with JSON Patch" href="/pageblocks/json-patch">
    Apply RFC 6902 operations to change a headline, add a section, or remove a block.
  </BrandCard>
</Columns>
