Admin UI
The admin renders from your schema at runtime. There are no generated page files: add a field and it appears.
View customization
Section titled “View customization”List columns
Section titled “List columns”Configure which columns appear in the list view via views in the collection definition:
defineCollection({ slug: "posts", views: { list: { columns: ["title", "category", "_status", "_updatedAt"], defaultSort: { field: "_updatedAt", direction: "desc" }, }, }, fields: { ... },});Field position
Section titled “Field position”Fields go to the content area by default. Set admin.position: "sidebar" to place a field in the sidebar:
fields: { title: fields.text({ required: true }), // → content body: fields.richText(), // → content slug: fields.slug({ admin: { position: "sidebar" } }), // → sidebar category: fields.text({ admin: { position: "sidebar" } }), // → sidebar}| Position | Description |
|---|---|
"content" |
Main area (left column on desktop), default |
"sidebar" |
Side panel (right column on desktop) |
Field groups
Section titled “Field groups”Set admin.group to render fields inside a titled panel in the edit form. Consecutive fields
sharing the same group become one panel; fields without a group render loose, as before.
Grouping only changes the form; field order and storage are unchanged.
fields: { heroHeading: fields.text({ label: "Heading", admin: { group: "Hero" } }), statValue: fields.text({ label: "Value", admin: { group: "Stats" } }), statLabel: fields.text({ label: "Label", admin: { group: "Stats" } }), notes: fields.text(), // ungrouped}Combine groups with short labels to turn a long flat form (for example a fixed-slot landing
page with many sections) into labeled panels instead of a wall of prefixed field names.
The object form makes a panel collapsible (rendered as a native <details> element).
collapsible: true starts open, "collapsed" starts closed. Once an editor toggles a
panel, the browser remembers that state per collection and group (localStorage), overriding the
schema default on later visits. Fields in the same run may
mix the string and object forms; the first collapsible declaration wins:
fields: { statValue: fields.text({ admin: { group: { label: "Stats", collapsible: true } } }), statLabel: fields.text({ admin: { group: "Stats" } }), archiveNote: fields.text({ admin: { group: { label: "Archive", collapsible: "collapsed" } } }),}Preview
Section titled “Preview”Collections with pathPrefix get a Preview link automatically. For collections without a prefix, add preview: true. For singletons, set preview to the URL:
// Automatic: pathPrefix enables previewdefineCollection({ slug: "posts", pathPrefix: "blog", ... });
// Explicit: no pathPrefix, needs opt-indefineCollection({ slug: "pages", preview: true, ... });
// Singleton: set the URL directlydefineCollection({ slug: "front-page", singleton: true, preview: "/", ... });The Preview link opens the public page in a new tab, showing draft content. While that tab is open, edits in the form update it live without saving. The page needs data-cms attributes on the elements it renders; see Live preview.
Custom field components
Section titled “Custom field components”Create a React component in src/cms/fields/ and reference it by name:
// In your collection definitioncolor: fields.text({ admin: { component: "ColorPicker" },});import type { CustomFieldProps } from "@kidecms/core";
export default function ColorPicker({ name, value, readOnly,}: CustomFieldProps) { return ( <input type="color" name={name} defaultValue={value || "#000000"} disabled={readOnly} /> );}The component receives name (form field name), field (field config), value (serialized value), and readOnly — the CustomFieldProps type. It renders with client:load and must include an input with the name prop so the form can read its value.
The integration scans src/cms/fields/ for .tsx files and registers each file’s default export under its file name, so ColorPicker.tsx is admin.component: "ColorPicker". Adding a file needs a dev-server restart; the directory is optional and can be absent.
Built-in admin.component variants, by field type:
| Field type | Component | Renders |
|---|---|---|
select |
"radio" |
Options as radio buttons instead of a dropdown |
text |
"taxonomy-select" |
Term picker fed by a taxonomy — see Taxonomies |
text |
"color" |
Palette swatch picker (what fields.color() sets) |
json |
"repeater" |
Sortable rows; typed with itemFields — see Fields |
json |
"link" |
Internal/external link control (what fields.link() sets) |
json |
"menu-items" |
Nested menu tree editor — see Menus |
json |
"taxonomy-terms" |
Nested term tree editor — see Taxonomies |
Any other name is looked up in src/cms/fields/.
Edit bar
Section titled “Edit bar”Logged-in editors see an “Edit this page” chip in the corner of public pages that links straight to the document’s edit form. It is on by default; turn it off with admin.editBar: false.
A page opts in by marking the document it renders with data-cms-doc="<collection>:<id>" — typically on <body> in the layout:
<body data-cms-doc={`pages:${doc._id}`}>Anonymous visitors make no extra requests: the client only calls /api/cms/edit-bar when a kide-editor hint cookie (set on admin visits) is present, and the endpoint checks the session and read access. The chip renders in a shadow root, so site CSS doesn’t affect it.
AI features
Section titled “AI features”Set AI_PROVIDER=openai and AI_API_KEY to enable AI buttons in the admin (AI_MODEL defaults to gpt-4o-mini; openai is the only provider):
- Alt text on asset detail pages
- SEO descriptions on edit forms
- Translation: per-field “Translate from EN” buttons, for plain and rich text
Without these variables the buttons are hidden and no AI code loads.
Custom navigation
Section titled “Custom navigation”Add custom pages to the admin sidebar via admin.nav in your CMS config:
export default defineConfig({ admin: { nav: [ { label: "Dashboard", href: "/dashboard", icon: "Home", weight: 10 }, { label: "Analytics", href: "/analytics", icon: "BarChart", weight: 20 }, { label: "Settings", href: "/settings", icon: "Settings" }, ], }, collections: [...],});| Option | Type | Description |
|---|---|---|
label |
string |
Display text in the sidebar |
href |
string |
Link URL |
icon |
string |
Lucide icon name (optional, defaults to grid) |
weight |
number |
Sort order within the Custom group (optional, default 50) |
The sidebar sorts by group first, then by weight within each group. Groups render in a fixed order:
| Order | Group | Contents |
|---|---|---|
| 0 | Content | Content collections |
| 10 | Library | Assets and related library items |
| 20 | Team | Users and team management |
| 100 | Custom | Your admin.nav items |
Custom nav items always land in the Custom group, after every built-in group; weight only orders them within it.
Collections set their own placement with the collection-level admin options; see Admin sidebar.
The linked pages are regular Astro pages you create in your app. To use the admin layout, import it from @kidecms/core:
---import AdminLayout from "@kidecms/core/admin/layouts/AdminLayout.astro";---
<AdminLayout title="Analytics | Admin"> <h1>Analytics</h1> <!-- your content --></AdminLayout>Available icons: BarChart, Bell, Bookmark, Calendar, Clock, Database, FileText, FolderTree, Globe, Home, Image, Inbox, Key, Layers, LayoutGrid, Link, Link2, Lock, Mail, Menu, MessageSquare, Package, Palette, PencilRuler, Search, Settings, Shield, Star, Tag, Terminal, Users, Zap.
Admin config
Section titled “Admin config”These options go under admin in cms.config.ts. Every admin option is listed in Configuration.
export default defineConfig({ admin: { uploads: { allowedTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf", "application/zip"], maxFileSize: 100 * 1024 * 1024, // 100 MB }, rateLimit: { maxAttempts: 10, windowMs: 5 * 60 * 1000, // 5 minutes }, }, collections: [...],});Uploads
Section titled “Uploads”| Option | Type | Default | Description |
|---|---|---|---|
allowedTypes |
string[] |
Images, PDF, MP4, WebM | Allowed MIME types |
maxFileSize |
number |
52428800 (50 MB) |
Max file size in bytes |
Default allowed types: image/jpeg, image/png, image/gif, image/webp, image/avif, application/pdf, video/mp4, video/webm.
SVG is not allowed by default because it can run script. Read Uploads and files before adding it to allowedTypes.
Rate limiting
Section titled “Rate limiting”| Option | Type | Default | Description |
|---|---|---|---|
maxAttempts |
number |
5 |
Login attempts before blocking |
windowMs |
number |
900000 |
Time window in ms (default 15 min) |
List views page at a fixed 10 items; the page size is not configurable.
Colors
Section titled “Colors”admin.colors is the palette offered by every color field; see Colors.
Date & time
Section titled “Date & time”Control how dates and times display throughout the admin.
| Option | Type | Default | Description |
|---|---|---|---|
dateFormat |
string |
"en-US" |
BCP-47 locale for date/time display (e.g. "en-GB", "fi-FI") |
timeZone |
string |
browser | IANA time zone (e.g. "Europe/Helsinki"). Overrides each viewer’s zone |
dateTimeFormat |
Intl.DateTimeFormatOptions |
— | Overrides merged over the defaults (numeric date + 2-digit HH:mm) |
dateTimePattern |
string |
— | Explicit token pattern; wins over dateFormat/dateTimeFormat |
admin: { dateFormat: "fi-FI", timeZone: "Europe/Helsinki", dateTimePattern: "d.M.yyyy HH:mm", // → 1.7.2026 14:30}dateFormat is a locale, not a pattern — it controls ordering and 12/24-hour conventions. For finer control use dateTimeFormat (e.g. { hour12: false } for 24-hour time, { second: "2-digit" } to show seconds) or dateTimePattern for an exact layout.
Pattern tokens: yyyy yy · MM M · dd d · HH H (24-hour) · hh h (12-hour) · mm m · ss s · a (AM/PM). Wrap literal text in single quotes:
| Pattern | Output |
|---|---|
d.M.yyyy HH:mm |
1.7.2026 14:30 |
dd.MM.yyyy 'klo' HH:mm |
01.07.2026 klo 14:30 |
h:mm a |
2:30 PM |
timeZone still applies to pattern output.
All settings are optional — defaults apply when omitted.