Content Studio
Content Studio is the structured admin UI at /admin/cms. It lets editors
browse entries, manage relationships, edit fields that do not appear on a page,
and restore earlier revisions.
What you can do here
Section titled “What you can do here”The home view
Section titled “The home view”/admin/cms shows a card for each known collection plus a “Create Collection” card. Cards come from three sources:
- Registered schemas in
caret()config - Dynamic collections created through Studio
- Discovered collections (anything with at least one entry on disk)
Click a collection card to drill into its entries.
Registered collections can set their label, description, icon, position, and
mutation capabilities in caret({ collections }). A collection with
singletonId opens its fixed entry directly. Cards can show a thumbnail from
images[0], coverImage, or portrait, and show Published/Unpublished when an
entry has a published field.
When a collection has no entries yet, Studio shows adapter-agnostic guidance — run Caretize to tag templates or create an entry from inline editing.
Editing an entry
Section titled “Editing an entry”Inside a collection, you see a list of entries. Click one and Studio renders a form generated from the schema:
| Field type | Renders as |
|---|---|
string |
Text input (long strings get textareas) |
number / integer |
Number input with min/max |
boolean |
Toggle |
enum |
Select dropdown |
object |
Grouped section with nested controls |
array |
Typed repeatable rows with add/remove, drag, and keyboard movement |
format: url / email |
Input with validation |
Nested objects and arrays keep their real controls: image previews and uploads, number inputs, switches, URLs, rich text, nested groups, and nested lists. New rows use schema defaults, receive focus, and use a useful field-derived label.
The sticky action area keeps Save, History, Delete, and Preview site visible while you scroll and on narrow screens. It always reports whether changes are saved, unsaved, or saving, and whether Save writes live or to a private preview. Studio asks for confirmation before the first live save in a browser session.
Studio validates required fields, types, ranges, and nested values before a write. Server validation repeats the same checks and returns field paths for any issues. If one stored entry is invalid, public collection reads skip it while Studio identifies the entry and fields that need repair.
Studio uses optimistic locking — if someone edited the entry while you were typing, you get a conflict toast and can deliberately keep yours or load the latest value.
Studio and page synchronization
Section titled “Studio and page synchronization”When Studio is open beside a page, clicking a Studio field scrolls to and marks its page match. Clicking a page field opens the correct collection, entry, and nested field in Studio. This works in the embedded sidebar and between separate Studio and preview tabs.
Text and image changes preview on the page while you type in the sidebar. Structural saves reload the page preview without closing Studio or losing the selected entry. Saves in another tab refresh the current view without replacing an unsaved inline edit.
Draft, preview, and publish
Section titled “Draft, preview, and publish”Inline edits and Studio saves go into a per-editor draft overlay first. Published storage (.caret/data/ or src/content/*.md) does not change until you click Publish in the editor toolbar.
| Action | Where | What happens |
|---|---|---|
| Save / inline edit | Editor toolbar or Studio | Writes to draft overlay |
| Preview | Editor toolbar | Toggle to see draft content on the live page |
| Publish | Editor toolbar | Flushes draft overlay into base storage |
| Discard | Editor toolbar | Drops draft without touching published content |
After Publish on static delivery sites, run astro build (or let CI rebuild via delivery.publish.webhookUrl) so visitors see the update.
Git commit on publish
Section titled “Git commit on publish”Set CARET_GIT_ON_PUBLISH=true in the environment where authoring runs. After a successful publish, CaretCMS best-effort commits the storage path (filesystem or markdown content root) with a message like publish pages/home. Git failures never block publish.
Revision history
Section titled “Revision history”Each save creates a revision snapshot you can restore from the history panel. Prior versions show timestamps and action labels (save, put, delete, reorder, restore). Restoring loads that version into the editor — save again to make it current. When an external identity provider supplies a named editor, new snapshots include that identity. Markdown publish history also preserves the prior body source so prose restoration is reversible.
Collection capabilities
Section titled “Collection capabilities”Capabilities belong to caret({ collections }) or dynamic collection metadata,
not to JSON Schema:
caret({ collections: { posts: { creatable: true, orderable: true, deletable: true }, site: { singletonId: 'global', creatable: false, deletable: false }, },})creatableshows New entry and seeds it from the schema template. Entry IDs are permanent internal addresses and are validated before creation.orderableenables pointer drag and keyboard movement. Studio validates every affected entry before writing any reordered entry.deletablecontrols Delete and is enforced server-side.singletonIdopens one fixed entry and suppresses collection-list creation UX.
See Configuration for labels, descriptions, icons, and ordering.
Accessibility and language
Section titled “Accessibility and language”Studio provides labelled form controls, announced save/error states, keyboard
row movement, screen-reader names for upload and row buttons, clearer contrast,
and mobile-safe navigation. English and Spanish chrome ship with core, and each
message can be overridden through locale and dictionary.
Schema sources
Section titled “Schema sources”Studio determines field types and labels from one of three sources, in priority order:
- Registered —
caret({ schemas: {...} }) - Dynamic — created via Studio’s collection builder
- Inferred — guessed from the first stored entry
The schema endpoint (/api/cms/schema?collection=pages) returns a source field telling you which one was used. See Schemas for the full picture.
API calls behind the UI
Section titled “API calls behind the UI”Everything Studio does is a public API call you can reproduce from a script:
| Route | Purpose |
|---|---|
GET /api/cms/entries?collection=... |
List entries |
GET /api/cms/schema?collection=... |
Schema + template |
GET /api/cms/collections-metadata |
All dynamic collection metadata |
POST /api/cms/mutate |
Execute a mutation command |
POST /api/cms/publish |
Flush draft overlay to base storage |
DELETE /api/cms/draft |
Discard draft without publishing |
GET /api/cms/history?collection=...&id=... |
Revision history |
POST /api/cms/upload |
Upload an image or asset |
See API Reference for full payloads.
Example mutation
Section titled “Example mutation”curl -X POST http://localhost:4321/api/cms/mutate \ -H 'Content-Type: application/json' \ -H 'x-caret-request: 1' \ -H 'Cookie: caret_session=<your-token>' \ -d '{ "type": "save_field", "collection": "pages", "id": "home", "field": "hero.headline", "value": "Ship faster with CaretCMS", "expectedRevision": 7 }'