Skip to content
Wiki— browse docs
On this page

Step 2: Scaffold it

One command, two source files, and a page that already reads your world.

You need the vvd CLI and a world to run it against. If you don't have either yet, install the CLI first — it takes about a minute and needs nothing else on your machine.

vvd create Chronicle --wiki
Expected output:
→ Creating wiki Chronicle in /Users/you/chronicle

✦  Chronicle — a brand-new wiki, ready to come alive.

Next:
cd chronicle
vvd run    # render it live in your world — hot-reloads as you edit
vvd save   # save a new version (a private draft)
✓ Created Chronicle (wiki) → /Users/you/chronicle

There's exactly one wiki base today, so vvd create <name> --wiki is the whole command — --template has nothing to choose between.

Note:

No live preview for this step. Scaffolding writes real files to a real filesystem and needs a real world to run against — nothing this browser tab can do. The terminal output above is the honest whole of it; Step 3 picks back up with something you can run right here.

What you got

chronicle/
├── vvd.json           the manifest — identity, class, capabilities
├── src/
│   ├── app.tsx         the page (live + published) and defineApp
│   └── publish-hooks.ts   bake — the live world → one frozen edition
├── docs/               reference material for a coding agent working in this project
└── .claude/skills/vvd-app/SKILL.md

No package.json, no lockfile, no node_modules. React, Yjs and @vvd/sdk are platform singletons the host injects when your bundle loads. There's no src/codec.ts either — a tool declares the document it edits; a wiki edits no document of its own, so it has none.

What makes it a wiki

A wiki isn't a third kind of thing — vvd has tool and app, full stop. A wiki is an app wearing four extra manifest lines:

vvd.json (the wiki-specific fields)
{
  "kind": "app",
  "category": "site",
  "publish": "custom",
  "capabilities": { "readsWorld": true, "sharing": true }
}
LineWhat it buys you
category: "site"Classes the app as a wiki template — it shows up in a world's template switcher and mounts in the wiki chrome (design controls beside the page, Publish top-right).
publish: "custom"Your bundle exports its own bake + renderBaked (Step 7). Without it, the platform substitutes its own surface for yours.
capabilities.sharingGrants the sitePublish capability — the thing that lets a wiki publish its own public address.
capabilities.readsWorldGrants world + search, so useWorldQuery can read the world's documents. Miss this and your wiki mounts to a loud, named error instead of a blank page — never a silent one.

vvd create --wiki writes all four. You'll rarely touch them by hand — vvd set name "The Chronicle" and vvd info cover the rest.

Your toolkit is @vvd/sdk

Every hook and every wiki-specific export in this build-along — useWorldQuery, WIKI_THEME_PARAM, useAppCustomization, all of it — comes from @vvd/sdk, the same package every tool and app is built on. There's no separate "site SDK" on the developer surface: the two built-in templates (Default, Overworld) are first-party code on internal packages that aren't injected into a third-party bundle and can't be imported from one. Reference has the full export list.

src/app.tsx, in shape

One file, four parts, in this order — the shape every step from here builds on:

  1. The page — the component both live and published render. Everything it needs arrives as props: resolved theme values, page state, write actions (or null), which entry is open.
  2. The live mount — reads the two things that only exist live (customization, collab state) and hands them down as props.
  3. The published face — the same page, fed from a frozen edition instead. actions={null}, because a frozen edition takes no writes.
  4. defineApp — the declaration, including publish: { bake, renderBaked }.
Danger:

The one rule this shape protects you from

Anything both lives render must never call useCollabState or useAppCustomization directly — a published page has no collab document and no Customize panel. Pass those values in as props from the live mount instead. The scaffold is already built this way; every step below keeps it that way.

Next steps