Skip to Content
DocumentationCMS Reference

CMS reference

Hyperclay CMS creates editing controls from rules that describe content in an HTML page. The page remains the content store. The CMS reads values from its elements and writes edits back to them.

For a complete first example, use the CMS guide. This page explains the model, rule syntax, controls, and JavaScript API.

Content: Rules, Rich text, Lists, Views, Field controls, Templates, Images.

Integration: Load the CMS, JavaScript API, Open options, Events, Saving, Coding-agent prompt, Hosted AI.

The page, the rules, and the editor

A rules tag gives names to the content you want to edit:

<h1 class="name">Alex Rivera</h1> <p class="bio">Making small things for the web.</p> <script data-rules-name="cms" data-rules-version="1" type="application/json"> { "name": ".name", "bio": ".bio" } </script>

The CMS reads the named elements into a temporary editing model and builds controls for those fields. Changing a control writes to the corresponding page element.

There is no separate CMS database to synchronize. The CMS controls content editing; HTML Clay or Hyperclay supplies saving and uploaded-file storage. This page covers the CMS API, not building a custom save host.

Load the CMS

<script src="https://clayjs.com/v1/clay.js?plugins=cms"></script>

Use a classic script tag for the loader, without type="module". ClayJS exposes the CMS as clay.cms. Put initialization after that loader and wait for clay.ready before calling the CMS:

clay.ready.then(() => { clay.cms.open({ view: 'inline' }); });

The script and its imported modules need a network connection unless you serve a complete local copy. Copying only clay.js does not include those modules. Packaging a self-hosted ClayJS distribution is outside this CMS reference.

Edit mode and dependencies

On a page in edit mode with a cms rules tag, the floating Edit content control provides entry to both views. HTML Clay 1.9.0 or newer supplies a savetoken on the root of a registered document’s served HTML. Opening a file inside your home folder through the app registers it; durable folder trust is not required for that file. Files outside your home folder cannot be opened through HTML Clay. ClayJS recognizes the token as edit mode and loads the editing and saving modules. Older HTML Clay versions use a token this ClayJS build no longer accepts; update the app rather than forcing edit mode.

The CMS and crop library also load outside edit mode, but RichClay, the host uploader, and saving do not. If you open a CMS session there, text elements use popovers instead of in-place RichClay editing, and image or file picks can embed data URLs. Native inputs mapped through @value remain native controls. See Saving and ownership for the permission boundary.

Build compatibility

This reference targets the two-view HyperCMS 0.10.0 build. After readiness, typeof clay.cms.currentView === 'function' checks for its view API. An older sidebar-only bundle lacks that method. Waiting for clay.ready does not upgrade the bundle. The API table describes the method’s return values.

Rules

Use type="application/json" and data-rules-version="1". The default rule source is the cms token in data-rules-name. A tag can carry multiple space-separated tokens, such as data-rules-name="cms api".

Opening a new session throws if no tag matches the requested token or its rules version is unsupported. Correct the tag before calling open() again.

Rule valueContent it describes
".name"Element text
"a.website@href"A link destination
"img.photo@src"An image source
".price@data-cents"An HTML attribute
"input.agree@checked"A checkbox’s checked state
"input.name@value"A native input’s current value
".bio@innerHTML"Markup inside an element
".tag[]"A list of scalar values
[".card", { "title": ".title" }]A list of objects, with selectors scoped to each card
{ "name": ".name", "bio": ".bio" }A group of fields

Map a text field to the element holding that text. Mapping a large wrapper as one text field can replace its child structure when you edit it. Prefer a separate rule for each piece of content.

Use unique leaf keys across the rules tree. In this build, repeating a key in different groups, such as title and metadata.title, can bind both fields to the first generated control and overwrite both targets. Use distinct keys such as pageTitle and metadataTitle.

Rich text

An explicit @innerHTML rule preserves markup within the selected element. Use it when retaining formatting is part of the field’s contract.

With richText enabled, the default, a bare scalar selector is also upgraded to @innerHTML when its first matching target contains child elements. An explicit @property or scalar-list rule ending in [] is never automatically upgraded.

The sidebar and inline views differ inside object lists. The sidebar does not automatically upgrade those rules. Inline mode does: if any row’s matching target contains markup, that child rule is upgraded for every row. For consistent behavior in both views, write @innerHTML explicitly on those fields.

The inline toolbar offers formatting only when the field’s rule can preserve it. The richText option disables automatic upgrading when set to false; it does not turn an explicit @innerHTML rule into plain text.

Lists

An object list uses [itemSelector, shape]. Each matching element is one item, and the shape’s selectors run within that item:

<style> [cms-template] { display: none; } </style> <ul class="links"> <li class="link"> <a href="https://example.com">My projects</a> </li> <li class="link" cms-template> <a href="https://example.com">New link</a> </li> </ul> <script data-rules-name="cms" data-rules-version="1" type="application/json"> { "links": [".link", { "label": "a", "url": "a@href" }] } </script>

The editor supplies add, remove, and reorder controls. New items copy the first existing item’s structure. The cms-template item is excluded from content data and supplies the structure when the list is empty. It belongs inside the list container.

In this example, a new link starts with an empty label and URL. The seed supplies structure, not default field values.

Hide the seed with [cms-template] CSS, as above, not a hidden attribute or inline display: none. Cloning removes the cms-template marker, so the new content item becomes visible. A copied hidden attribute would keep it invisible.

Without an existing item or a matching seed, adding an item fails with EmptyListInsert and leaves the page unchanged. Add a seed inside the list container before retrying.

Inline and sidebar views

Both views use the same rules and editing session.

ViewInteraction
Inline, On the pageClick text to edit it in place. Handles beside images, links, and other fields open their controls in popovers. Visible lists have nearby add, remove, and move controls.
Sidebar, In the sidebarEdit the fields together in a generated form beside the page. It can expose fields without a visible target in the page layout.

In inline mode, writable inputs, textareas, and selects mapped through @value keep their native controls. Text elements use RichClay, the text editor loaded by ClayJS. Images and attribute fields use popovers. Text also falls back to a popover if RichClay is unavailable or cannot attach to the target, such as a table element or a non-HTML element.

The floating bar’s editable areas number counts field handles, not all editable content. It does not count text edited in place or native inputs. Resolved fields whose elements are hidden or too small to target get a separate message beside Edit in the sidebar; choose that action to reach them in the form. A rule that matches no element is unresolved, not part of that message’s count; correct its selector or add the missing content.

Hide controls hides the inline list controls. It does not close the editing session. Close editor ends the session and keeps edits in the current DOM. Saving is a separate action.

The floating menu remembers the selected view for the origin. A programmatic open() starts a new session in the sidebar. If a session is already open, bare open() preserves its view. Use open({ view: 'inline' }) or open({ view: 'sidebar' }) to choose explicitly.

The URL parameter ?cms=true opens the default editor; it does not specifically select inline mode or grant saving permission. Outside edit mode, the dependency limits still apply.

Refresh behavior

The CMS subscribes to ClayJS’s document-wide mutation observer, then rereads content within pageRoot, document.body by default, after 100 ms without another qualifying change. The observer ignores library controls and excluded regions such as no-watch. Continuous changes can postpone the automatic refresh; call clay.cms.refresh() when your integration needs an explicit refresh.

Normal refreshes preserve the focused form field’s in-progress value. Undo and redo deliberately replace that value with the reverted content.

Field controls

The rule and the page element determine the default control. @src selects an image upload control, @checked selects a checkbox, and @innerHTML selects a rich text control. Set data-hcms-component on a target element to request a specific control.

ComponentConfiguration
textareaA multiline text field
richtextA rich text control; use @innerHTML to retain its markup
numberA numeric input; getData() returns "5", not 5, for a value of five
checkbox or toggleA boolean field
select or radioAdd space-separated values such as data-hcms-options="low medium high"; use a custom template for values containing spaces
image or fileAn upload field
chipsOn a scalar list’s container, a compact editable list

A field-specific <template data-hcms-tpl="path"> overrides the default control.

Within a repeating rule, control attributes are sampled from the first matching element, so configure corresponding elements consistently.

If a numeric field contains a value such as $5, it falls back to a plain text control so opening the editor does not erase that value. A checkbox or toggle on a rule other than @checked accepts true, false, or an empty value; other values fall back the same way. An accepted empty value reads as unchecked; the next commit writes false back to the mapped content, even if another field caused that commit.

Custom form templates

Use a template when you need a control or form layout that the named components do not supply. Put it in the page’s body before opening the CMS; lookup uses data-hcms-tpl, not its position beside a field. This overrides the sidebar control for bio:

<template data-hcms-tpl="bio"> <label> <span data-hcms-label></span> <textarea data-hcms-field rows="6"></textarea> <span class="hcms-error" hidden></span> </label> </template>

For a field or container, lookup uses the first matching template:

  1. Exact path, such as links.0.label.
  2. Wildcard path, such as links.*.label.
  3. Selected component or shape default.

By default, .photo@src selects @image, while a plain text field uses @scalar. Overriding @scalar does not replace an inferred image control; use a path-specific template or override @image for that.

Template keyScope
linksThis list’s container
links.*Each item in this list
links.*.labelThe label field within each item
@scalar, @objectDefault plain field or field group
@scalar-array, @object-arrayDefault list containers
@scalar-array-item, @object-array-itemDefault list items
@image, @textarea, and other component keysFields using that component

List items have a separate lookup order:

  1. Wildcard item template, such as links.*.
  2. The item template for the component used by the list container, if it supplies one.
  3. Shape default, such as @object-array-item.

A path-specific list container and its item template are separate overrides.

Leaf and object-list-item templates can supply their own fields. For a field group, use a slotted template with .hcms-object-fields; self-supplied child fields in a group template read as null and can erase the group’s page content on the next commit.

For scalar-list item layouts, retain the field used by the selected item default. With the standard scalar-list default, use input[data-hcms-field] in a per-list template such as tags.*. Replacing it with a textarea there can produce null items and erase their text after an unrelated edit. To change that field type, override @scalar-array-item instead; this affects every scalar list using that default.

The markers below identify the controls and slots for generated fields and list items.

Marker inside the templatePurpose
data-hcms-fieldThe element holding a scalar value: input, textarea, select, image, link, or contenteditable element
data-hcms-field="label"Bind a child field by key inside an object-list-item template
data-hcms-labelWhere the generated label appears
data-hcms-shape="object-array" or "scalar-array"Required on a custom list root for removal confirmation and item-limit handling
data-hcms-action="add" or "remove"List add or remove button
data-hcms-action="move-up" or "move-down"List reorder button
.hcms-object-fieldsContainer for generated child fields in a slotted field-group template
.hcms-card-fieldsContainer for generated child fields in a slotted object-list-item template
.hcms-array-itemsDirect child of the rendered list root; generated item roots must be direct children of this slot
.hcms-errorError display slot; retain it in custom controls

A custom list container must preserve that direct-child relationship:

<template data-hcms-tpl="links"> <section data-hcms-shape="object-array"> <div class="hcms-array-items"></div> <div class="hcms-error" hidden></div> <button type="button" data-hcms-action="add">Add link</button> </section> </template>

After changing a template, compare clay.cms.api.getData() with the existing page content before editing or saving. Unexpected null values or empty lists indicate a binding problem; fix the template first. These mistakes can commit successfully and erase existing content, so do not rely on onError to catch them.

On a list’s <template>, data-hcms-min-items and data-hcms-max-items set the editor’s item limits. data-hcms-no-add, data-hcms-no-remove, and data-hcms-no-reorder hide those operations. These configure the editor, not server permissions or validation for writes outside it.

For per-list deletion confirmation, put data-hcms-confirm-remove on the root element inside the custom array template, not on <template> itself. That root must also carry the matching data-hcms-shape from the table above.

An empty confirmation attribute uses the default prompt. A string supplies the prompt; off, false, no, or 0 disables it, ignoring case and surrounding whitespace for those disabling values. This overrides the session’s confirmRemove option.

Images and cropping

Map the source and opt into cropping on the image:

<img class="photo" src="portrait.jpg" alt="Portrait" data-hcms-crop="1:1">
{ "photo": ".photo@src" }

data-hcms-crop="1:1" requests a square crop. Use another width-to-height ratio or free for an unconstrained crop. An image field without that attribute does not automatically acquire cropping.

The upload destination belongs to the save host. HTML Clay stores accepted uploads beside the document, stripping its extension to form the folder name: profile.htmlclay uses assets-profile. Hyperclay uses its hosted upload service. A host without upload support may use an embedded data URL instead, increasing the HTML file’s size. An upload error is not the same as a successful fallback; check the displayed result.

JavaScript API

These methods are available on clay.cms after clay.ready resolves:

MemberBehavior
open(options)Open a session or switch its view; calling it for the active view is a no-op
close()End the session without saving the page
refresh()Refresh the active editor from the page
isOpenBoolean getter
currentView()"inline", "sidebar", or null when closed
api.getData()Current form data, or null when closed
api.setValue(path, value)Write a scalar leaf through the active session
api.addItem(arrayPath)Add an item to a list
api.removeItem(itemPath)Remove the indexed list item without asking for confirmation

The action methods open, close, refresh, setValue, addItem, and removeItem return undefined, not a Promise or a success result. Observe changes and errors through the callbacks or events below.

For the list example, api.getData() returns:

{ "links": [{ "label": "My projects", "url": "https://example.com" }] }

Paths use dots and numeric indices: links.0.url. setValue accepts a scalar leaf, not an entire object or list. setValue, addItem, and removeItem throw synchronously when the session is closed. Invalid paths can also throw before a commit; onError does not catch those API-use errors.

confirmRemove and data-hcms-confirm-remove apply to the editor’s remove controls, not api.removeItem(). A custom delete button must ask for confirmation itself if you want one. The API still honors list limits: removal stops at data-hcms-min-items or data-hcms-no-remove; addition stops at data-hcms-max-items or data-hcms-no-add. Those blocked operations return without changing the page or throwing an error.

clay.ready.then(() => { clay.cms.open({ view: 'inline' }); clay.cms.api.setValue('name', 'Alex R.'); clay.cms.open({ view: 'sidebar' }); });

Open options

OptionDefaultPurpose
viewExisting view, otherwise "sidebar"Select "inline" or "sidebar"
rules"cms"A rules token or an explicit rules object
pageRootdocument.bodyRoot for extracting and applying content
richTexttrueAllow rich text upgrading
themenull"light" or "dark" in either view; otherwise follow the system preference
confirmRemoveConfirmation for object-list cards, none for scalar lists including chipsFor editor remove controls: true requests confirmation, false skips it, and a string supplies the prompt; a list’s data-hcms-confirm-remove overrides this option
onChange(data, info)NoneReceive changed data and its path/structural metadata
onError(error)NoneReceive commit errors

For a successful CMS commit, info.path identifies the edited field or affected list/item path. info.structural is true for add, remove, and reorder operations, and false for scalar edits. Editing a link URL produces info such as { path: 'links.0.url', structural: false }. An undo or redo that changes CMS data also calls onChange, with path: "" and structural: false. Undoing unrelated page content does not call it.

Sidebar presentation options:

OptionDefaultAccepted value and behavior
mountTodocument.bodyA DOM element to contain the sidebar
side"right""left" or "right"
overlayfalseBoolean; overlay the page instead of shifting it
showSaveButtonfalseBoolean; show a button carrying trigger-save. ClayJS handles that click in edit mode; a custom host must provide its own handler.
title"Page content"String shown in the sidebar heading
eyebrow"Edit"String shown above the heading

Switching views carries the current session’s options. Close and reopen when you need to replace the session’s rules or root.

Events

hcms:open, hcms:change, hcms:error, and hcms:close bubble to the document. Their detail includes pageRoot and view. Change events also include data, path, and structural; the last is true for operations such as adding or removing list items.

A view switch dispatches hcms:close with reason: "switch", then hcms:open for the new view. The open event’s previous value names the old view; it is null on the first open. Use these values to distinguish switching from closing and reopening.

Undo and redo that change CMS data call onChange, but do not dispatch hcms:change.

Error events include error. Commit error objects identify their kind in error.name, such as EmptyListInsert. Commit failures also include attemptedData, the form data the CMS tried to apply to the page. Inspect it when diagnosing a rejected edit; correct the cause before retrying.

Failed structural commits restore a snapshot of the affected list container’s content, or non-editor page content when that container cannot be resolved. Scalar commits do not take a rollback snapshot. After an unexpected scalar failure, inspect the affected content before retrying; do not assume every failed commit is atomic.

Upload failures dispatch hcms:error, not onError. Read event.detail.code and event.detail.path; the code is not a property of the error object. The path identifies the affected field. Common codes are too-large for the host’s size limit, unsupported-type for a rejected file type, and timeout when the result is unknown. The code comes from the uploader’s result, so handle unknown codes as a generic upload failure. For the local retry procedure, see guide troubleshooting.

Use these events to observe the session. They do not replace the host’s save result.

Saving and ownership

The CMS edits the DOM. ClayJS and the host handle saving. Use Cmd+S on macOS or Ctrl+S on Windows and Linux, or add the autosave attribute to <html>. Add indicator to the loader’s plugin list for visible save feedback, as in the guide. Runtime editor elements and session markers are removed by the snapshot pipeline, so they do not become the saved content.

The saved document retains the content and the configuration needed to edit it again. Uploaded assets may remain separate files. Moving the HTML alone does not move those assets.

Host permission determines who can persist changes. Showing editing controls is not an access-control boundary. The same distinction applies to collaboration: CMS fields can refresh when the page changes, but live synchronization requires a host and runtime that provide it.

Configure with a coding agent

Give an AI coding agent your HTML and this request:

Add Hyperclay CMS rules for the human content in this page. Preserve its layout, styles, scripts, and existing content. Map content such as headings, body text, image sources, and link destinations to its elements. Load the CMS through ClayJS. Do not map navigation controls or application machinery as editable content. Use this reference: https://docs.hyperclay.com/docs/cms-reference . Show me the changes.

Review the rules and try each field. Generating configuration does not verify that its selectors point at the content you intended.

Generate CMS with AI on Hyperclay

Add CMS with AI is the hosted platform’s paid configuration action. The model proposes content structure; the platform validates it and applies the corresponding rules and supporting markup. The CMS then handles edits in the browser using that configuration.

Review the resulting fields on your page. Regenerate CMS with AI reruns configuration; do not treat it as a substitute for reviewing changes. The CMS library can also be configured directly, without the hosted AI service.

Last updated on