Add a select slot
Give the user a closed set of choices — a slot whose value is one of a vocabulary you write out or one a bound fact supplies.
Give the user a closed set of choices: a slot whose value is one of a vocabulary you write out, or one the fact it is bound to already supplies.
Prerequisites
Section titled “Prerequisites”- A section to add the slot to — Install scaffolds one.
- Its
fixtures.tsbuilt from a scenario, so the fact behind the slot resolves in preview — Pick a fixture scenario.
A select slot still needs a source binding, exactly
like every other slot type — the vocabulary alone only declares the closed set the
editor’s dropdown offers. The cleanest no-AI fill for a closed set is usually a
fact that already resolves to one of a closed set of strings. Write the
vocabulary out as select.of’s first argument and bind the value with source.
Add select and fact to the section’s existing kit import — the scaffolded file
takes only defineSchema, text and textBlock, so both are missing
until you do.
// section.ts — add a select slot, seeded from a closed-vocabulary factproperty_type: select.of( ["single_family", "condo_townhome", "multi_family", "investment"], { label: "Property type", source: fact.property.property_type, writeback: false },),An option is a bare string when the stored key and the label are the same thing, or
{ value, label } when they differ. select.of is the hand-authored vocabulary;
select({ source: … }) — no array — instead points the dropdown at the bound fact’s
own domain, which any direct fact binding supplies (see
the schema declaration).
// Renderer.tsx — supply the label yourself; the stored value is an internal keyconst PROPERTY_TYPE_LABEL: Record<string, string> = { single_family: "Single family", condo_townhome: "Condo / townhome", multi_family: "Multi-family", investment: "Investment",};
<Slot.Select slot={slot.property_type} as="span"> {PROPERTY_TYPE_LABEL[slot.property_type.value]}</Slot.Select>The kit ships no catalog of option labels, so the mapping is yours to supply — as
children here, or as a format callback. Supply neither and the stored key renders as its
own text, which is legible but rarely what you want on a page. Slot.Select never emits a
text leaf, which is
the point: the stored value (single_family) is an internal key, and leafing it would let a
keystroke in the editor overwrite the key instead of changing which label is shown for it.
Slot.Url withholds the leaf for the same reason — see
values that never leaf.
slot.property_type.value is string — an unfilled slot arrives as the
blank "", and slot.property_type.present is the absence signal. The general source
contract still applies to a
select slot, but no fill-spec decision type constrains its output to the closed
vocabulary — so a source binding onto a fact that already resolves within the set is
the reliable way to fill one.
Complete diff
Section titled “Complete diff”Two additions — one in section.ts, one in Renderer.tsx — plus select and
fact on the section’s existing kit import:
// section.ts — in `defineSchema`'s `slots`property_type: select.of( ["single_family", "condo_townhome", "multi_family", "investment"], { label: "Property type", source: fact.property.property_type, writeback: false },),// Renderer.tsx — the label map, then the call site wherever the value belongsconst PROPERTY_TYPE_LABEL: Record<string, string> = { single_family: "Single family", condo_townhome: "Condo / townhome", multi_family: "Multi-family", investment: "Investment",};
<Slot.Select slot={slot.property_type} as="span"> {PROPERTY_TYPE_LABEL[slot.property_type.value]}</Slot.Select>Verify
Section titled “Verify”Verify with the author loop.
Rules that can fire
Section titled “Rules that can fire”schema-invalid— the slot declares a vocabulary but carries nosourcebinding.missing-slot-marker— the new editable slot never reaches the rendered DOM with its marker.sidebar-order— the slot is declared in one position in the schema and rendered in another.
See also
Section titled “See also”- The slot types — the slot-type table, including
where
selectsits next totext/number/etc. - The schema declaration — the full contract, including
factbinding. - Bind slot to fact — the
sourcebinding this slot is filled through.