# Quickstart (/docs/start/quickstart)

> Render a worked Business's home page from one JSON record under two Themes, make it yours, and ship it.

**Using an agent? Paste this prompt**

```text
Read https://next.mdxui.dev/docs/start/quickstart.md and https://next.mdxui.dev/llms.txt.
Download https://next.mdxui.dev/starter.zip and unzip it. Run pnpm install, then pnpm render.
Rewrite the strings of pages/home.json for my Business. Keep the Section types that fit.
Parse it with PageSchema.safeParse. When it refuses, shorten the content at the path it names, never the bound.
Render it under its own Theme and one more, and show me both HTML files.
```

Copy a five-line prompt: your agent downloads the starter, rewrites the record for your Business, fixes each refusal at its path and renders it under two Themes.

You start from Vanroster, a worked B2B SaaS on these docs, and leave with its home page as two HTML files. Or take the finished files:

[Download starter.zip](https://next.mdxui.dev/starter.zip): `pages/home.json`, `render.ts` and a `package.json`.

## Prerequisites

- Node.js 22.18 or later, which runs `render.ts` with no build step.
- An empty directory that is not inside another pnpm project. Mark it as an ES module first:

```bash
pnpm init && npm pkg set type=module
```

The starter download above already holds a `package.json` with `"type": "module"`; with it, run `pnpm install && pnpm render` and skip to step 3.

**File: install**

```bash
pnpm add @mdxui/site@7.0.0-alpha.0
```

**File: pages/home.json**

```json
{
  "$schema": "https://next.mdxui.dev/schema.json",
  "theme": "callboard",
  "frame": "none",
  "template": "api-product",
  "title": "Vanroster: every crew on the right job",
  "description": "Crew scheduling for field-service teams. Vanroster matches each job to a crew with the skills, the van and the shortest drive.",
  "sections": [
    {
      "kind": "nav",
      "shell": { "inset": "bleed", "density": "none", "surface": "glass", "sticky": true },
      "block": {
        "type": "nav",
        "menu": "push",
        "collapse": 880,
        "brand": { "href": "/", "text": "Vanroster", "mark": "radar" },
        "links": [
          ["/product/dispatch", "Dispatch"],
          ["/pricing", "Pricing"],
          ["/changelog", "Changelog"],
          ["/security", "Security"]
        ],
        "cta": { "href": "/contact", "label": "Book a walkthrough" }
      }
    },
    {
      "kind": "hero",
      "shell": { "inset": "contained", "density": "hero", "background": "glow" },
      "block": {
        "type": "hero",
        "layout": "split",
        "stack": 640,
        "title": "Every crew on the right job.",
        "lede": "Vanroster matches each job to a crew with the right skills, the right van and the shortest drive. Move a job on the board, and every crew phone follows.",
        "actions": [
          { "href": "/contact", "label": "Book a walkthrough", "kind": "solid" },
          { "href": "/pricing", "label": "See the plans", "kind": "ghost" }
        ],
        "fine": ["14-day trial, no payment details", "Office users free on every plan"],
        "specimen": {
          "panel": "field",
          "blocks": [
            {
              "type": "terminal",
              "variant": "plain",
              "title": "Board · Tue 13 Oct · 07:42",
              "lines": [
                "@dim{CREW  VAN   JOB                 WINDOW        DRIVE}",
                "A-1   V-12  Boiler service      08:00-10:00   14 min",
                "A-1   V-12  Heat pump install   10:30-13:00    9 min",
                "@dim{B-2   V-07  Out today: 2 jobs moved}",
                "C-3   V-03  Panel upgrade       08:00-11:00   22 min",
                "C-3   V-03  @acc{Boiler service}  ←B2 11:30-12:30    @pass{6 min}",
                "D-4   V-15  @acc{Gas leak check}  ←B2 13:00-14:00   @pass{11 min}",
                "D-4   V-15  Water heater swap   14:30-16:30   18 min",
                "",
                "@pass{2 customers texted a new window}"
              ]
            }
          ]
        }
      }
    },
    {
      "kind": "band",
      "shell": { "inset": "contained", "density": "compact" },
      "block": {
        "type": "stats",
        "items": [
          { "value": "400", "label": "Crews on one board, on every plan", "icon": "layout-grid" },
          { "value": "12", "label": "Skills a job can require, from gas to height", "icon": "badge-check" },
          { "value": "30 min", "label": "Arrival window texted to each customer", "icon": "clock" }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "feature-grid",
        "cellHead": "icon",
        "head": {
          "eyebrow": "The board",
          "title": "One board runs the whole day.",
          "lede": "Office staff plan on the board. Crews work from one ordered list on their phones."
        },
        "items": [
          {
            "icon": "map-pin",
            "title": "Drive time, not distance",
            "text": "Jobs are ordered by the drive between them, from road times at the hour of the job."
          },
          {
            "icon": "badge-check",
            "title": "Skills before speed",
            "text": "A gas job reaches only a crew with a gas licence on file and in date."
          },
          {
            "icon": "refresh-cw",
            "title": "Swaps in one drag",
            "text": "Drag a job to another crew. Both crew phones update before the drag ends."
          },
          {
            "icon": "bell",
            "title": "Customers told once",
            "text": "Each customer gets one text with a 30-minute window, and one when the van leaves."
          },
          {
            "icon": "clock",
            "title": "Overtime flagged",
            "text": "A day that keeps a crew past 10 hours turns amber before anyone assigns it."
          },
          {
            "icon": "shield-check",
            "title": "Every change logged",
            "text": "Who moved which job, when and why, kept for up to 7 years."
          }
        ]
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section", "surface": "tint" },
      "block": {
        "type": "feature-split",
        "head": { "eyebrow": "A sick call at 07:40", "title": "Re-plan the day in one move." },
        "items": [
          { "icon": "x", "title": "Mark the crew out", "text": "One tap clears the crew and lists every job it held that day." },
          { "icon": "radar", "title": "Accept the re-plan", "text": "Each job goes to the nearest crew that holds every skill it needs." },
          { "icon": "message-square", "title": "Tell the customers", "text": "Each changed window goes out as one text, with the new time." }
        ],
        "specimen": {
          "blocks": [
            {
              "type": "terminal",
              "variant": "plain",
              "title": "Text to the customer · 07:43",
              "lines": [
                "Your boiler service today moved to 11:30-12:30.",
                "Crew C-3 is coming in van V-03.",
                "",
                "@dim{A second text arrives when the van leaves.}",
                "@dim{Reply 1 to keep the time, 2 to pick another day.}"
              ]
            }
          ]
        }
      }
    },
    {
      "kind": "section",
      "shell": { "inset": "contained", "density": "section" },
      "block": {
        "type": "compare",
        "head": { "eyebrow": "Against the whiteboard", "title": "A board that moves with the day." },
        "cons": {
          "label": "Whiteboard and calls",
          "items": ["Jobs assigned from memory", "Licences checked by phone", "Customers call to ask", "Swaps lost by noon"]
        },
        "pros": {
          "label": "Vanroster",
          "items": ["Jobs matched to skills", "Licences checked on assign", "Customers get a window", "Every swap on record"]
        }
      }
    },
    {
      "kind": "closing",
      "shell": { "inset": "contained", "density": "closing", "surface": "plate" },
      "block": {
        "type": "closing",
        "layout": "split",
        "head": {
          "eyebrow": "14-day trial",
          "title": "Plan tomorrow on Vanroster.",
          "lede": "Import crews and jobs from three spreadsheets. The first board is ready the same afternoon."
        },
        "specimen": {
          "blocks": [
            {
              "type": "terminal",
              "variant": "plain",
              "title": "Import · 3 files",
              "lines": [
                "crews.csv    14 crews, 14 vans, 2 depots",
                "skills.csv   38 licences, @acc{3 expire this year}",
                "jobs.csv     212 jobs over the next 10 days",
                "",
                "@pass{Board ready: 212 jobs on 14 crews}"
              ]
            }
          ]
        },
        "actions": [
          { "href": "/contact", "label": "Book a walkthrough", "kind": "solid" },
          { "href": "/product/dispatch", "label": "Tour Dispatch", "kind": "ghost" }
        ]
      }
    },
    {
      "kind": "footer",
      "shell": { "inset": "contained", "density": "footer" },
      "block": {
        "type": "footer",
        "layout": "areas",
        "brand": { "href": "/", "text": "Vanroster", "mark": "radar" },
        "blurb": "Crew scheduling for field-service teams: heating, electrical, plumbing, cleaning and grounds.",
        "columns": [
          {
            "title": "Product",
            "links": [
              ["/product/dispatch", "Dispatch"],
              ["/pricing", "Pricing"],
              ["/changelog", "Changelog"]
            ]
          },
          {
            "title": "Company",
            "links": [
              ["/security", "Security"],
              ["/contact", "Contact"]
            ]
          },
          {
            "title": "For agents",
            "links": [
              ["/product/dispatch", "The Dispatch API"],
              ["/records.zip", "Site records (.zip)"]
            ]
          }
        ],
        "cta": { "href": "/contact", "label": "Book a walkthrough", "icon": "arrow-right" },
        "note": ["Vanroster is a worked example on mdxui.dev, not a customer.", "Rendered by @mdxui/site. Forms here do not send."]
      }
    }
  ]
}
```

**File: render.ts**

```ts
// Renders each record in pages/ twice: under its own Theme, and under ALSO.
// A string over its bound stops the run and names the field.
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
import { PageSchema, renderPage } from '@mdxui/site';

// certificate, vaultline, quaypoint, callboard or directory
const ALSO = 'vaultline';

const pages = [];
for (const file of (await readdir('pages')).filter((f) => f.endsWith('.json'))) {
  const json = JSON.parse(await readFile(`pages/${file}`, 'utf8'));
  const result = PageSchema.safeParse(json);
  if (!result.success) {
    for (const { path, message } of result.error.issues) {
      console.error(`pages/${file}: ${path.join('.')}: ${message}`);
    }
    process.exit(1);
  }
  pages.push({ name: file.replace(/\.json$/, ''), page: result.data });
}

await mkdir('out', { recursive: true });
for (const { name, page } of pages) {
  await writeFile(`out/${name}.html`, renderPage(page));
  await writeFile(`out/${name}.${ALSO}.html`, renderPage(page, { theme: ALSO }));
  console.log(`out/${name}.html  ${page.theme}`);
  console.log(`out/${name}.${ALSO}.html  ${ALSO}`);
}
```

**File: Result**
The record as this site renders it, under each Theme</span>}>
The result, as this site renders it: https://next.mdxui.dev/pages/vanroster/home.json

## Install

Install `@mdxui/site` at its exact version. It brings `@mdxui/themes` at the same version, so you name one package.

The bare `mdxui` package shows a higher version, `7.0.0-alpha.1` from May 2026, but it is an older, unrelated line. Install `@mdxui/site@7.0.0-alpha.0`; [Packages and versions](https://next.mdxui.dev/docs/start/packages) lists every name to avoid.

## Write the record

Copy Vanroster's home record into `pages/home.json`. It runs from the nav to the footer, and it parses as it stands.

## Render it under two Themes

Save `render.ts` and run `node render.ts`. `renderPage` returns one HTML document per record: once under the record's own `theme`, and once under the Theme in `ALSO`.

```text title="node render.ts"
out/home.html  callboard
out/home.vaultline.html  vaultline
```

The words are the same in both files. The color, typefaces, dividers and plan styling come from the Theme.

## Make it yours

Set `theme` to the Theme your Brand wears, and rewrite the hero `title` as your Business's promise: 64 characters fit, and the `lede` under it takes 260. Run `node render.ts` again and open `out/home.html`.

Long copy never breaks the layout. If the schema says no, the run names the field, such as `sections.1.block.title`, and writes nothing; shorten the content at that path. [Refusals](https://next.mdxui.dev/docs/reference/refusals#hero.title) lists every bound.

## Result

`out/home.html` is Vanroster's home page in your words. The Result tab renders the same record on this site: pick a Theme chip to see it in each of the five.

## Ship it

Your page is one HTML file that any static host serves. [Ship a Site](https://next.mdxui.dev/docs/start/ship) turns a folder of records into a whole Site with a 404, on Node, Vite, React Router or a Worker.

## Next steps

  - [Ship a Site](https://next.mdxui.dev/docs/start/ship): A folder of records as one live Site with a 404.
  - [Build a landing page](https://next.mdxui.dev/docs/start/guides/landing-page): Every section of the home page, nav to footer.
  - [Write a Theme](https://next.mdxui.dev/docs/themes/write-a-theme): Your Brand's tokens and policies, under every record.
  - [Have your agent write a page](https://next.mdxui.dev/docs/agents/write-a-page): The same loop, with an agent writing the record.

