• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
c15r

c15r

sync

Agent collaboration layer https://sync.parc.land
Public
Like
1
sync
Home
Code
41
.claude
2
.claude-plugin
2
docs
13
frontend
13
mcp
13
reference
10
scripts
11
skills
1
static
1
.gitignore
.mcp.json
.vtignore
CLAUDE.md
INSTALL.md
PLUGIN.md
README.md
actions.ts
agents.ts
audit.ts
auth.ts
cel.ts
context.ts
deno.json
deps.ts
docs.ts
help-content.ts
invoke.ts
H
main.ts
meta.ts
poll-v8.ts
replay.ts
rooms.ts
sampling.ts
schema-v7.ts
schema-v8.ts
schema.ts
timers.ts
tokens.ts
utils.ts
views.ts
wait.ts
Connections
Environment variables
2
Branches
8
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
reference
/
surfaces.md
Code
/
reference
/
surfaces.md
Search
3/6/2026
Viewing readonly version of main branch: v638
View latest version
surfaces.md

Surfaces Reference

Surfaces are declarative UI components rendered by the dashboard. In v6, the canonical way to create surfaces is via views with render hints — register a view with a render object and the dashboard renders it automatically.

See Views Reference for the full views + render hints documentation.

Two mechanisms (backward compatible)

v6: Views with render hints (recommended)

Register a view with a render object:

POST /rooms/my-room/actions/_register_view/invoke
{ "params": {
    "id": "score",
    "expr": "state[\"_shared\"][\"score\"]",
    "render": { "type": "metric", "label": "Score" },
    "enabled": "state[\"_shared\"][\"phase\"] == \"active\""
}}

The dashboard queries views with render defined and renders them as surfaces. No _dashboard config blob needed. enabled expressions are evaluated server-side.

Legacy: _dashboard config in state

Existing rooms may use the older pattern of storing a DashboardConfig object at state._shared._dashboard. The dashboard detects this and renders the surfaces array. This still works for backward compatibility.

Surface types

Every surface has id (unique string), type, and optional enabled (CEL expression — surface is hidden when false). Most have an optional label.

markdown

Renders a view's string value as Markdown.

{ "id": "story", "type": "markdown", "view": "narrative", "label": "Story" }
  • view — ID of a registered view whose resolved value is a string.

metric

Large single-value display. Good for KPIs and counters.

{ "id": "score", "type": "metric", "view": "player-score", "label": "Score" }
  • view — ID of a registered view.

view-grid

Horizontal card row showing multiple view values.

{ "id": "stats", "type": "view-grid", "views": ["fire-status", "wood-count", "temperature"], "label": "Status" }
  • views — array of view IDs. Each renders as a card with label + value.

view-table

Vertical key/value table layout for multiple views.

{ "id": "info", "type": "view-table", "views": ["location", "health", "mana"] }
  • views — array of view IDs. Each renders as a row: label | value.

action-bar

Row of buttons, one per action. Auto-hides actions where available: false.

{ "id": "explore", "type": "action-bar", "actions": ["look_around", "try_door"], "label": "Explore" }
  • actions — array of action IDs.

action-form

Single action, always expanded with its parameter form visible.

{ "id": "chat", "type": "action-form", "action": "send_message", "label": "Say something" }
  • action — single action ID.

action-choice

Mutually exclusive buttons (no-param actions only).

{ "id": "direction", "type": "action-choice", "actions": ["go_north", "go_south"], "label": "Which way?" }
  • actions — array of action IDs.

feed

Filtered message stream with optional compose input.

{ "id": "chat", "type": "feed", "kinds": ["chat"], "compose": true, "label": "Chat" }
  • kinds — filter messages by kind field. Omit for all.
  • compose — show/hide the compose input. Defaults to true.

watch

Raw state key/value display.

{ "id": "inv", "type": "watch", "keys": ["inventory", "equipment"], "label": "Inventory" }
  • keys — array of strings (_shared scope) or { "scope": "...", "key": "..." } objects.

section

Container that groups child surfaces with conditional visibility.

{ "id": "outdoor-section", "type": "section", "label": "Outside", "enabled": "state._shared.outside == true", "surfaces": [ { "id": "gather", "type": "action-bar", "actions": ["gather_wood"] } ] }
  • surfaces — nested array of any surface types.

Design patterns

See Surfaces as Substrate for the full design principles: absence is signal, locality of reasoning, additive composition, gate state vs display state, self-describing components.

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.