The settings contract
A controllable demo declares exactly one module-level object named settings
and takes it as props:
src/demos/default.tsx
- a module-level
const settings = { … }object literal, and - a default export that accepts props and merges them over
settings.
settings but no props would render knobs that move nothing, so it
gets no panel at all.
At publish time the values inside that literal become the demo’s defaults. Only
a JSON-ish subset is read: strings, numbers, booleans, null, arrays, and
nested objects. A value the parser does not understand (a function call, a
spread, a template string, an imported identifier) is skipped per key, not per
file, so one exotic value does not cost you the whole panel.
Nested paths are not addressable. Every control drives one top-level key
of
settings. Group knobs visually instead of nesting data.What the panel looks like
If you author nothing else, 21st infers the panel from your values:
Inferred labels are humanized keys (
particleCount → “Particle count”) and
inferred ranges are guesses. Every value is clamped to its range before it
reaches the preview or the copied code, so an inferred panel is always safe —
it is just not always well named. That is what editing it is for.
A panel supports up to 6 groups, 24 controls, and 12 presets.
Editing the panel after publishing
Open the component’s Edit dialog (component page → top-right menu, or press E). Under each demo, the Controls section shows the panel and is labelled Detected (inferred) or Edited (authored by you). There you can rename any row and retune a slider’smin, max, and step.
Those live in the database, not in the bundle, so a mislabelled knob is a
rename, not a re-publish — saves apply as you type. Reset returns the panel to
the detected one.
Grouping, presets, and dropdown options are part of the same schema, but they
are curated by 21st rather than edited here; labels and ranges you saved are
kept when a richer panel lands on your demo.
Demos that own their state
A canvas loop, a particle engine, or anything that reads its props once on mount will not react to a changed prop. The panel can rebuild the demo subtree instead of updating it in place for exactly those controls, which is one of the things curation sets. If your demo ignores a knob that clearly maps to a real setting, that is why.What the visitor gets
- Live preview — changes drive the published bundle directly; no rebuild, no round trip.
- A shareable link — the tuned state travels in the preview URL’s fragment
(
#c=…), and only the diff against your defaults is encoded, so an untouched panel produces the usual URL. - Copy code that matches — copying writes the tuned values back into the
settingsliteral of the source they take. Comments, formatting, and any value 21st did not parse survive byte for byte.
Checklist
- One module-level
const settings = { … }per demo. - The default export takes props and merges them over
settings. - Keep the keys flat and give them names that read well as labels.
- Ship sensible default values — they are what the marketplace card, the cover, and the copied code all start from.
- After publishing, open Edit and spend a minute on the labels and ranges inference guessed.