⚠️ Alpha · testnets only. ChainRender is in active alpha and deployed on testnets only — Ethereum Sepolia, the Robinhood Chain Testnet, and the Circle Arc Testnet — not on any mainnet. Contracts, addresses, and APIs may change. Do not use with real funds or production assets.
Reference

Reference: The Vocabulary

The vocabulary is the closed set of everything a ChainRender app can express. It's machine-readable via vocabulary(), and it's closed by design — that closure is what makes apps analyzable and safe. Every list in it is derived from the real constants (the on-chain format dictionaries, the capability bits, the resolver-kind mirrors), so tooling never hardcodes them.

import { vocabulary } from "@chainrender/client";
const v = vocabulary();
// v.nodeTypes      the 9 node types and their own fields
// v.a11y           accessibility fields (every node type except image and ref)
// v.behavior       cond / step / anim / live (every node type except ref)
// v.enums          allowed values: role, ariaState, variant, direction, inputType, provenance, anim, animRepeat
// v.capabilities   the capability bits
// v.kinds          IR sub-vocabularies: chart, control, layout
// v.resolverKinds  every first-party resolver kind, with its args (most of the payload)
// v.limits         maxNodes, maxDepth

Node types

The compiler reads exactly nine node types: text, box, button, input, icon, link, slot, image and ref. Everything else you build is made of these:

  • Headings, notes, captions are text with a role (compose.heading(...) is a text with role: "heading").
  • Charts, tables, controls (toggle, select, slider, search), layouts (stack/grid/flow) and multi-view apps are structured renderers with their own sub-vocabularies in vocabulary().kinds, built with the format builders (buildTabs, buildWizard, …) and rendered by their own on-chain views.
  • Live data is a slot (text) or an image (media) bound to a resolver kind.

Every field of every node type — which ones are required, the allowed enum values, which a11y and behaviour fields apply — is listed on Node Fields, generated from the same table the compiler is tested against.

Checking a tree before you compile it

compile() is lenient: it silently drops anything it doesn't read. A misspelled enum value (direction: "vertical"), a role that isn't in the dictionary (role: "note"), an unknown field, a11y fields on an image, or args on a batch member all compile without an error — and ship without what you wrote. Check first:

import { checkNode, nodeJsonSchema } from "@chainrender/client";   // 0.10.0+

const r = checkNode(tree);              // { ok, issues: [{ path, code, message }], nodeCount, depth }
if (!r.ok) console.table(r.issues);     // e.g. /children/2/role  enum  role must be one of button, textbox, …

const schema = nodeJsonSchema();        // the same rules as a strict JSON Schema (draft 2020-12) for an AI tool

checkNode also enforces what JSON Schema can't express: at most 1024 nodes, depth at most 64, 255 children or attributes per node, and 65,535 bytes per literal. A tree made in the visual builder carries builder-only fields — argValues on slots and images (encoded into arg at export), image.scene, a root pinnedDep, and _-prefixed design-time keys such as _multiview; check it with checkNode(tree, { authoring: true }) (SDK 0.10.1+).

Capabilities

READ_CHAIN (read state), WRITE_CHAIN (send a tx), VALUE (move ETH), SIGN_MESSAGE (typed signing), BATCH_EXECUTE (atomic multi-action), CLIENT_FETCH (off-chain fetch — drops the grade), GPU_EXECUTE (client GPU enhancement, fails closed to a poster). An app declares its envelope; the enforcer verifies it.

Resolver kinds

Each resolver declares which kinds it serves. vocabulary().resolverKinds lists every first-party kind with its args, data edge and capability — never hardcode a kind string or guess an arg shape; read it from here. The full list is on All Resolver Kinds. Encode a slot's arg exactly as the kind says: abi.encode of its args unless the kind is marked argsEncoding: "packed" (raw bytes, e.g. ensName takes the 20 address bytes).

The kind list is most of the vocabulary's size, so an AI building one component rarely needs all of it. Through the MCP server, request the vocabulary without resolverKinds and look kinds up with chainrender_search_kinds / chainrender_describe_kind — see MCP Server & AI Skill.