Step 3: The world becomes pages
Read the world's documents as entries, live — no content of your own, ever.
Your wiki stores nothing. There's no codec, no document type, nothing to seed. Every entry on the page is a document someone made in the world, read through the host at render time — which is what makes a wiki feel alive: edit a card in the editor and the wiki follows in the same second, with no sync code anywhere in your bundle.
Edit the tool below. Replace the placeholder with a live read of the world:
const docs = useWorldQuery("documents", { type: "card" })Edit and the example re-runs. Tab indents; press Escape to leave the editor.
You should see: two card names appear — this world (like every playground on this site)
has a couple of them already. useWorldQuery is live: on a real world, adding a card makes
it show up here without touching this code.
Honour isViewable, always
isViewable === false means the author marked that document as not for readers. Publishing
drops those documents outright, so if your live wiki shows them anyway, your live page and
your published page disagree — filter on d.isViewable !== false everywhere, and use
!== false rather than === true since older rows may not carry the field at all.
At the scale it actually matters
Two cards don't show much. Here's the same idea — cards as entries, everything else on a shelf below — against a small world with a map, a table and four cards in it. This one's read-only (there's no code to edit; it's here to show scale, not to practice on), and its code, running right below it:
function WikiFrontPage() {
const world = useWorldMeta()
const docs = useWorldQuery("documents")
const entries = useMemo(
() => docs.filter((d) => d.documentType === "card" && d.isViewable !== false),
[docs],
)
const extras = useMemo(
() => docs.filter((d) => d.documentType !== "card" && d.isViewable !== false),
[docs],
)
return (
<>
<strong>{world?.name ?? "An encyclopedia"}</strong>
{entries.map((e) => <span key={e.id}>{e.name}</span>)}
{extras.map((d) => <span key={d.id}>{d.name} · {d.documentType}</span>)}
</>
)
}Rendering an entry, and its image
Your bundle has no card editor, no map renderer. Ask the host to render a document by id instead — the real tool for that type mounts, read-only, using codecs your bundle has never seen:
<HostEmbed documentId={open.id} view="embed" />And a media id is never a URL you build yourself — the bytes might be public, private, or
belong to a different world entirely. Paint media.placeholder(id) immediately (synchronous,
best-effort), then overwrite it with media.resolve(id) once it resolves. Reference
has the two-line pattern and the classic bug it prevents.
Recap
- A wiki stores nothing: every entry is a document in the world, read through the host at render time.
useWorldQuery("documents", { type: "card" })is live and filters at the source.HostEmbedmounts the real tool for a document type your bundle has never seen.isViewable === falsenever ships — filter live the same way publishing filters, or the two will disagree.