Blocks reference
<!-- Generated by pnpm docs:build (docs-reference) from packages/blocks/src/config.tsx and meta.ts. Do not edit by hand; edit the source and run the command. -->
Every block of @spillway-cms/blocks with its editor fields, options and default props. Blocks are Puck components: the admin shows the fields in the right-hand panel and the server renders the props with @puckeditor/core/rsc. Props marked resolved are attached by the server at render time and never stored. The narrative guide is blocks.md.
Summary
| Block | Category | Description |
|---|---|---|
| Columns | Layout | Two to four side-by-side columns, each a slot that holds other blocks. Stacks on small screens. |
| Spacer | Layout | Vertical space (s, m, l, xl). |
| Divider | Layout | A horizontal rule. |
| Heading | Content | A standalone heading (h1 to h4) with alignment. |
| Text | Content | Body copy. Plain text split into paragraphs on blank lines, or raw HTML. |
| Image | Content | A single image with required alt text, optional caption and width (normal, wide, full). |
| Embed | Content | Raw embed HTML (for example a YouTube iframe) in a responsive 16:9 frame. |
| Button | Content | A single button with alignment; its emphasis (primary, secondary or a text link) comes from its style. |
| Hero | Marketing | Large opening section with an eyebrow, title, subtitle, up to two buttons and an optional background image. |
| CallToAction | Marketing | A title, short text and one button; its style (a colored band, a bordered card or one the site defines) comes from Brand → Styles. |
| Cards | Marketing | A grid of cards with title, text, optional link and image, in 2 to 4 columns. |
| Collection | Marketing | A saved query (collection) of entities rendered as cards, a list or a grid, optionally with visitor filters (keyword, taxonomy, select, date range) and a pager; items are filled in by the server at render time. |
| Calendar | Marketing | A month grid or date list of the event occurrences in a collection, with a link to its iCal feed; filled in by the server at render time. |
| Form | Marketing | A lead-capture form defined under Forms in the admin, referenced by key. Fields, labels and the success message come from the definition. |
| Shared | Marketing | A block saved in the component library (Shared blocks in the admin), referenced by key and rendered by the server; edit it once, update everywhere. |
| Personalize | Marketing | Two slots, Default and Variant; visitors who match an audience (Audiences in the admin) see the variant. The server decides per visitor after the page loads, so pages stay cached. |
| Experiment | Marketing | An A/B test: slots a (control), b and optional c; visitors of a running experiment (Experiments in the admin) are assigned a variant by weight and the results screen reports conversions per variant. |
| RegistryBrowse | Marketing | Search and browse the package registry (modules, themes, kits): a GET search form, kind tabs, a sort, result cards with badges and a pager; filled in by the registry service from the page URL. Placeholder on instances that do not host the registry. |
| RegistryPackage | Marketing | The detail page of one registry package, resolved from the last segment of the page path (/registry/<name>): header with badges, install command with a copy button, publisher and links, capability sheet, compatibility, versions, advisories, deprecation notice, README, changelog and a "For agents" section linking manifest.json. |
| RegistryStats | Marketing | The registry totals: packages by kind, publishers and downloads, as a row of figures. |
| ThemeGallery | Marketing | The theme directory: registry kits of kind "theme" as cards with a screenshot, description, publisher and Preview and Details links. |
| PublisherProfile | Marketing | A publisher’s public profile (handle, name, verified badge, bio, website, maintainers) and their packages; the handle comes from the page path (/registry/publishers/<handle>) or the handle prop. |
| OrgProfile | Marketing | An organization’s public profile (handle, name, verified badge, website, members with their roles, the namespaces it owns) and the packages published under those namespaces; the handle comes from the page path (/registry/orgs/<handle>) or the handle prop. |
| DocsFeedback | Marketing | "Was this page helpful?" with Yes and No buttons and an optional comment, posted to a form (docs_feedback by default) with the page path and the documented version as hidden fields; works without JavaScript and asks for no personal data. |
| StatusBoard | Marketing | A list of Spillway instances with their state (up, degraded or down), version, when each was last checked and a 90-day uptime bar; the server fetches every instance’s /status.json (3 s timeout, cached for a minute) and reads the history the probe step records every five minutes. |
| ModuleBlock | Modules | A block an installed module provides (module name, block key and the block’s own props); rendered by the module on the server, with a placeholder when the module is disabled or missing. |
Layout
Columns
Two to four side-by-side columns, each a slot that holds other blocks. Stacks on small screens.
AI hint: Use to place blocks side by side, for example an Image next to a Text. Each entry in "columns" has a "content" array of child blocks. Keep to 2 or 3 columns for readability.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
columns | Columns | array | [{"content":[]},{"content":[]}] | |
columns[].content | Content | slot | child blocks | |
gap | Gap | select | "normal" | "normal" |
Spacer
Vertical space (s, m, l, xl).
AI hint: Use sparingly to add breathing room between sections. Blocks already have padding.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
size | Size | radio | "s", "m", "l", "xl" | "m" |
Divider
A horizontal rule.
AI hint: Use to separate unrelated sections. Do not place directly after a Spacer.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
width | Width | radio | "normal", "narrow" | "normal" |
Content
Heading
A standalone heading (h1 to h4) with alignment.
AI hint: Use to start a section. Prefer h2 for sections and h3 for sub-sections; the Hero already provides the h1. Keep headings short and specific.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
text | Text | text | "Section heading" | |
level | Level | select | "h1", "h2", "h3", "h4" | "h2" |
align | Alignment | radio | "left", "center", "right" | "left" |
Text
Body copy. Plain text split into paragraphs on blank lines, or raw HTML.
AI hint: Use for prose. Prefer format "paragraphs" and separate paragraphs with a blank line. Use "lead" size for a single intro paragraph. Only use "html" when markup such as lists or links is required.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
content | Content | textarea | "Write a paragraph here. Leave a blank line between paragraphs.\n\nThis is a sec | |
format | Format | select | "paragraphs", "html" | "paragraphs" |
size | Size | radio | "normal", "lead" | "normal" |
align | Alignment | radio | "left", "center", "right" | "left" |
Image
A single image with required alt text, optional caption and width (normal, wide, full).
AI hint: Use only with a real image URL from the media library (/media/...). Always write descriptive alt text; use an empty alt only for purely decorative images.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
src | Image URL | text | placeholder: /media/<id>/<filename> | "" |
alt | Alt text (required) | text | placeholder: Describe the image | "" |
caption | Caption | text | "" | |
width | Width | radio | "normal", "wide", "full" | "normal" |
rounded | Rounded corners | radio | true, false | true |
Embed
Raw embed HTML (for example a YouTube iframe) in a responsive 16:9 frame.
AI hint: Use only when the user provides embed code or a known embeddable URL. Put the full iframe markup in "html". Never invent video IDs.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
html | Embed HTML | textarea | placeholder: <iframe src="https://www.youtube.com/embed/..."></iframe> | "" |
caption | Caption | text | "" |
Button
A single button with alignment; its emphasis (primary, secondary or a text link) comes from its style.
AI hint: Use for a standalone link styled as a button. Label is a verb phrase; href is a site path or full URL.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
label | Label | text | "Learn more" | |
href | Link | text | "#" | |
align | Alignment | radio | "left", "center", "right" | "left" |
Marketing
Hero
Large opening section with an eyebrow, title, subtitle, up to two buttons and an optional background image.
AI hint: Use once, at the top of a landing page. Title under 10 words, subtitle one or two sentences. Primary button is the main action; secondary is optional. Leave backgroundImage empty unless a real image URL is known.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
eyebrow | Eyebrow | text | placeholder: Short label above the title | "Introducing" |
title | Title | text | "A headline that says what you do" | |
subtitle | Subtitle | textarea | "One or two sentences that explain the value and who it is for." | |
primaryLabel | Primary button label | text | "Get started" | |
primaryHref | Primary button link | text | placeholder: /get-started | "#" |
secondaryLabel | Secondary button label | text | "Learn more" | |
secondaryHref | Secondary button link | text | "#" | |
backgroundImage | Background image URL | text | placeholder: /media/... | "" |
align | Alignment | radio | "left", "center" | "center" |
size | Size | radio | "default", "large" | "default" |
CallToAction
Label in the editor: Call to action.
A title, short text and one button; its style (a colored band, a bordered card or one the site defines) comes from Brand → Styles.
AI hint: Use near the end of a page to ask for one action. Title under 8 words, text one sentence, a specific button label such as "Book a demo" rather than "Submit".
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
title | Title | text | "Ready to get started?" | |
text | Text | textarea | "Tell the reader what happens next and why it is worth their time." | |
buttonLabel | Button label | text | "Contact us" | |
buttonHref | Button link | text | "#" |
Cards
A grid of cards with title, text, optional link and image, in 2 to 4 columns.
AI hint: Use for features, benefits, services or a list of related pages. Three cards with parallel phrasing read best. Leave href empty when there is no target page.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
cards | Cards | array | [{"title":"First point","text":"Explain one benefit in a sentence or two.","href | |
cards[].title | Title | text | ||
cards[].text | Text | textarea | ||
cards[].href | Link | text | placeholder: /page or https://... | |
cards[].imageUrl | Image URL | text | placeholder: /media/... | |
columns | Columns | radio | 2, 3, 4 | 3 |
Collection
A saved query (collection) of entities rendered as cards, a list or a grid, optionally with visitor filters (keyword, taxonomy, select, date range) and a pager; items are filled in by the server at render time.
AI hint: Use for "latest posts", "case studies" or any list that should update itself. Set collectionId to the key of an existing collection (ask or leave empty if unknown); never invent items, the server supplies them.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
collectionId | Collection id or key | text | placeholder: latest_posts | "" |
display | Display | select | "cards", "list", "grid" | "cards" |
columns | Columns (cards and grid) | select | 2, 3, 4 | 3 |
showDate | Show date | radio | true, false | true |
showSummary | Show summary | radio | true, false | true |
emptyText | Text when empty | text | "Nothing here yet." | |
filters | Visitor filters | array | [] | |
filters[].field | Field key | text | placeholder: department | |
filters[].label | Label | text | ||
filters[].kind | Kind | select | "search", "taxonomy", "select", "daterange" | |
pageSize | Items per page | number (1..100) | 12 | |
showPager | Show pager | radio | true, false | false |
showResultCount | Show result count | radio | true, false | false |
Calendar
A month grid or date list of the event occurrences in a collection, with a link to its iCal feed; filled in by the server at render time.
AI hint: Use on an events page. Set collectionId to the key of an existing collection of a type with a daterange field (ask or leave empty if unknown); view "month" for a grid, "list" for upcoming dates.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
collectionId | Collection id or key | text | placeholder: upcoming_events | "" |
view | View | radio | "month", "list" | "month" |
monthsAhead | Months shown | number (1..12) | 1 |
Form
A lead-capture form defined under Forms in the admin, referenced by key. Fields, labels and the success message come from the definition.
AI hint: Use for contact, demo or newsletter forms. Set formKey to the key of an existing form (leave it empty if none is known); never invent fields here. Add a short title and a one-sentence intro.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
formKey | Form key | text | placeholder: contact (from Forms in the admin) | "" |
title | Title | text | "" | |
intro | Intro text | textarea | "" | |
submitLabel | Button label override | text | placeholder: Leave empty to use the form definition | "" |
Shared
Label in the editor: Shared block.
A block saved in the component library (Shared blocks in the admin), referenced by key and rendered by the server; edit it once, update everywhere.
AI hint: Use only when the user names an existing shared block key (a promo, a contact card). Never invent keys; leave sharedKey empty if none is known.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
sharedKey | Shared block key | text | placeholder: from Shared blocks in the admin | "" |
Personalize
Two slots, Default and Variant; visitors who match an audience (Audiences in the admin) see the variant. The server decides per visitor after the page loads, so pages stay cached.
AI hint: Use only when the user names an existing audience key. Put the content for everyone in "default" and the audience version in "variant"; keep both the same shape (a Hero in each, for example). Never invent audience keys.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
audienceKey | Audience key | text | placeholder: from Audiences in the admin | "" |
default | Default content | slot | child blocks | [] |
variant | Content for the audience | slot | child blocks | [] |
Experiment
An A/B test: slots a (control), b and optional c; visitors of a running experiment (Experiments in the admin) are assigned a variant by weight and the results screen reports conversions per variant.
AI hint: Use only when the user names an existing experiment key. Put the current content in "a" and the challenger in "b"; leave "c" empty unless asked. Never invent experiment keys.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
experimentKey | Experiment key | text | placeholder: from Experiments in the admin | "" |
a | Variant A (control) | slot | child blocks | [] |
b | Variant B | slot | child blocks | [] |
c | Variant C (optional) | slot | child blocks | [] |
RegistryBrowse
Label in the editor: Registry browse.
Search and browse the package registry (modules, themes, kits): a GET search form, kind tabs, a sort, result cards with badges and a pager; filled in by the registry service from the page URL. Placeholder on instances that do not host the registry.
AI hint: Use once on the /registry page of the project site. Keep the defaults; set kind to "theme" or "kit" only for a page dedicated to that kind. Needs no other props; the server supplies the results.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
kind | Default kind | select | "all", "module", "theme", "kit", "brand" | "all" |
pageSize | Results per page | number (1..100) | 20 | |
showSearch | Show search box | radio | true, false | true |
showKinds | Show kind tabs | radio | true, false | true |
showSort | Show sort | radio | true, false | true |
layout | Layout | radio | "list", "grid" | "list" |
emptyText | Text when nothing matches | text | "No packages match. Try another word or a different kind." |
RegistryPackage
Label in the editor: Registry package.
The detail page of one registry package, resolved from the last segment of the page path (/registry/<name>): header with badges, install command with a copy button, publisher and links, capability sheet, compatibility, versions, advisories, deprecation notice, README, changelog and a "For agents" section linking manifest.json.
AI hint: Use once on the /registry/<name> catch-all page of the project site. The package comes from the URL; there is nothing to fill in besides the section toggles.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
basePath | Registry base path | text | placeholder: /registry | "/registry" |
showReadme | Show README | radio | true, false | true |
showVersions | Show versions | radio | true, false | true |
showChangelog | Show changelog | radio | true, false | true |
showAgents | Show "For agents" section | radio | true, false | true |
notFoundText | Text when the package is unknown | text | "No package by that name. Check the spelling or search the registry." |
RegistryStats
Label in the editor: Registry stats.
The registry totals: packages by kind, publishers and downloads, as a row of figures.
AI hint: Use on the registry landing or home page of the project site to show how much is published. Leave the title empty when a Heading precedes it.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
title | Title (optional) | text | "" | |
showPublishers | Show publishers | radio | true, false | true |
showDownloads | Show downloads | radio | true, false | true |
ThemeGallery
Label in the editor: Theme gallery.
The theme directory: registry kits of kind "theme" as cards with a screenshot, description, publisher and Preview and Details links.
AI hint: Use on a /themes page of the project site. Set limit to the number of cards wanted; the server supplies the themes.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
title | Title | text | "Themes" | |
intro | Intro text | textarea | "A theme is a kit of design tokens, blocks and page templates. Preview one on a | |
limit | Themes to show | number (1..60) | 12 | |
columns | Columns | radio | 2, 3 | 3 |
showBrowseLink | Link to all themes | radio | true, false | true |
emptyText | Text when there are no themes | text | "No themes have been published yet." |
PublisherProfile
Label in the editor: Publisher profile.
A publisher’s public profile (handle, name, verified badge, bio, website, maintainers) and their packages; the handle comes from the page path (/registry/publishers/<handle>) or the handle prop.
AI hint: Use once on the /registry/publishers/<handle> catch-all page of the project site, with handle left empty. Set handle only for a page about one known publisher.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
handle | Publisher handle (empty = from the URL) | text | placeholder: acme | "" |
basePath | Publishers base path | text | placeholder: /registry/publishers | "/registry/publishers" |
layout | Package layout | radio | "list", "grid" | "grid" |
notFoundText | Text when the publisher is unknown | text | "No publisher by that handle." |
OrgProfile
Label in the editor: Organization profile.
An organization’s public profile (handle, name, verified badge, website, members with their roles, the namespaces it owns) and the packages published under those namespaces; the handle comes from the page path (/registry/orgs/<handle>) or the handle prop.
AI hint: Use once on the /registry/orgs/<handle> catch-all page of the project site, with handle left empty. Set handle only for a page about one known organization.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
handle | Organization handle (empty = from the URL) | text | placeholder: acme | "" |
basePath | Organizations base path | text | placeholder: /registry/orgs | "/registry/orgs" |
layout | Package layout | radio | "list", "grid" | "grid" |
showMembers | List the members | radio | true, false | true |
notFoundText | Text when the organization is unknown | text | "No organization by that handle." |
DocsFeedback
Label in the editor: Docs feedback.
"Was this page helpful?" with Yes and No buttons and an optional comment, posted to a form (docs_feedback by default) with the page path and the documented version as hidden fields; works without JavaScript and asks for no personal data.
AI hint: Put it last on a documentation page. Keep formKey at docs_feedback unless the site defines another feedback form; the docs kit creates that form and places this block on every doc page.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
formKey | Form key | text | placeholder: docs_feedback | "docs_feedback" |
question | Question | text | "Was this page helpful?" | |
yesLabel | Yes button | text | "Yes" | |
noLabel | No button | text | "No" | |
commentLabel | Comment label | text | "Tell us more (optional)" | |
thanksText | Thanks text | text | "Thanks for the feedback." | |
version | Documented version (recorded with the answer) | text | "" |
StatusBoard
Label in the editor: Status board.
A list of Spillway instances with their state (up, degraded or down), version, when each was last checked and a 90-day uptime bar; the server fetches every instance’s /status.json (3 s timeout, cached for a minute) and reads the history the probe step records every five minutes.
AI hint: Use on a /status page of the project site. Fill instances with a name and the instance’s public URL (https://...); never invent URLs. Keep showUptime on unless asked.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
title | Title (optional) | text | "Status" | |
instances | Instances | array | [] | |
instances[].label | Name | text | ||
instances[].url | Instance URL | text | placeholder: https://www.example.org | |
showUptime | Show the 90-day uptime bar | radio | true, false | true |
Modules
ModuleBlock
Label in the editor: Module block.
A block an installed module provides (module name, block key and the block’s own props); rendered by the module on the server, with a placeholder when the module is disabled or missing.
AI hint: Only use with a module and block key that GET /api/builder/config lists; never invent one. Fill props from that block’s field definitions.
| Prop | Label | Type | Options | Default |
|---|---|---|---|---|
module | Module | text | "" | |
blockKey | Block key | text | "" | |
props | Props | object | {} |
Spillway 0.1.0 · Edit this page on GitHub