Skip to content
Apps— browse docs
On this page

Step 6: Link the sketch to the world

Pin a finished drawing to a real card, by id — a sketch stops being an island.

Let's say you just sketched the room from the opening scenario. Right now that drawing lives only inside this one sketch document — nobody browsing The Sunken Vale's card would ever find it. Point the sketch at that world document and it becomes a linked artifact in the world graph instead.

Read the world, live

import { useWorldQuery } from "@vvd/sdk"

const cards = useWorldQuery("documents", { type: "card" })

That's it — no useEffect, no loading flag. cards is live: it re-renders when someone adds one anywhere in the world, on any client. Reading the world covers the whole API; this build uses one call from it.

Store an id, never a copy

src/codec.ts
export const codec = defineStateCodec({
  strokes: field.map<Stroke>(),
  linkedDocumentId: field.value<string | null>(null),
})

One field, and it's a reference — the same rule the first-tool tutorial used for a note's link. Rename the card later and this chip follows, because there's nothing to keep in sync; there's only one copy of the name, and it isn't on the sketch.

A sketch, linked to a card
Running on example data.
Starting the example…

You should see: draw a couple of strokes, then click the dashed Link a card chip in the bottom-left corner. This frame's world has Aria of the North, The Sunken Vale, The Salt Road, and The Cartographers' Guild in it — pick The Sunken Vale, and the chip turns solid, showing its name. Click it again to unlink.

Note:

The grant is two lines, the same two Getting Started's world-data page covers:

vvd.json
{ "capabilities": { "readsWorld": true }, "needs": ["world"] }

Forget readsWorld and useWorldQuery fails loudly the moment sketchTool mounts, naming the capability it wanted — never a silently empty list. See Reading the world for exactly what that failure looks like.

Recap

  • useWorldQuery("documents", { type: "card" }) reads the world's cards live and read-only.
  • A link is one field — linkedDocumentId: field.value<string | null>(null) — always an id.
  • Rename the linked card in the world and the chip's label follows; there's nothing to keep in sync.
  • capabilities.readsWorld + needs: ["world"] is the whole grant, and it's enforced every mount.

Next steps