Skip to content
Apps— browse docs
On this page

Step 6: Let people make their own board

The same picker vvd run shows in the terminal, built from inside the app itself.

Step 5 proved two boards can be independent. It didn't show you how anyone gets a second one. Right now that's a terminal question — vvd run asks "open which project?" before it ever shows you a browser. Here's that exact prompt, for real, in a real project:

$ vvd run
? Open Toy in which project? › (Use arrow keys)
❯ The Salt Road
  Winterhold
  + New — a fresh Toy

Pick one (or make one) and the dev tab opens already scoped to it — that's the mechanism behind Step 5's independence, driven from a terminal instead of two browser panes.

Your app can offer the same choice inside its own UI, with the capability that prompt is built on:

const projects = useHostCapability("projects")

const boards = await projects.list("toy") // every board this world has
const fresh = await projects.create("toy", "A new board")

Add a small header above the canvas that lists existing boards and makes new ones:

A picker of your own
src/app.tsx

Edit and the example re-runs. Tab indents; press Escape to leave the editor.

Running · your edits, live
Starting the example…

You should see: a thin bar above the board, naming your scope ("world canon" — a docs page mounts no project) and a + New board button. Click it a few times; the pill row fills up. Nothing is wired to actually switch boards here — that's the tab's job, covered next — but the list and the creation are real calls against the fake host, the same shape vvd run's prompt makes against the real one.

Warning:

needs: ['projects'] — new here

Every capability your tool touches gets declared. useCollabState didn't need one (collaboration is granted by default); projects does — it's how the install screen tells a user "this app can create instances of itself" before they say yes.

Switching, for real

Inside a real world, your app is mounted as a tab, and the tab knows which board it's bound to:

const tab = useHost().tab

tab?.setProject(chosen) // re-bind THIS tab — the host persists it and re-scopes you
tab?.setProject(null) // back to world canon

Read tab off the host rather than through useHostCapability — it only exists where there are tabs, and a docs page, an embed, and a published link all honestly have none. Projects covers tab and the projects capability end to end.

Next steps