⚠️ Work in progress — createCMS is pre-1.0 and not production-ready (not tested in production). Expect breaking changes.
createCMS
Guides

Editor UI

Install a CMS-wired editor page in one command, or copy styled chrome from the createCMS shadcn registry.

The createCMS docs site hosts a shadcn registry of copy-paste editor chrome. Start with editor-app for a complete CMS-wired page.

The chrome items (editor-form, editor-canvas, editor-shell, editor-email) do not wrap Editor.Root or Canvas.Root — compose them inside Editor.Root. For form-only, live canvas, and form-plus-preview layouts, see the Visual editor guide.

Prerequisites

Your app should already be initialized with shadcn (npx shadcn@latest init) and have @createcms/react installed:

npm install @createcms/react

editor-app handles the CMS wiring (useCmsDocument and useCmsFieldSources). Pass fields={cmsFields} on Editor.Root and wrap with CmsSourcesProvider only when you compose chrome yourself — pass the result of useCmsFieldSources(client) into the provider.

Start with one install

editor-app copies a complete CMS-wired editor page in one shadcn add.

npx shadcn@latest add https://createcms.dev/r/editor-app.json
'use client';

import { CmsEditor } from '@/components/editor-app';

export function PageEditor({
  rootId,
  branchId,
}: {
  rootId: string;
  branchId: string;
}) {
  return (
    <CmsEditor
      client={cmsClient}
      collection="pages"
      rootId={rootId}
      branchId={branchId}
      schema={pages}
      components={pageBlocks}
    />
  );
}

Copied files land in your shadcn components directory. Adjust the import if your aliases differ.

What CmsEditor includes:

  • Document load and save (useCmsDocument)
  • Field sources (useCmsFieldSources, CmsSourcesProvider, fields={cmsFields})
  • EditorShell and canvas overlays
  • Loading skeleton, error + Retry (doc.reload), and a conflict dialog on HEAD_MISMATCH (reload vs save({ force: true }))

mode="form" skips Canvas.Root and passes mode into EditorShell. Form mode hides device toggles, the palette/outline sidebar, Add block, and the inspector so FormSurface is the only edit surface. The form-only demo is this path: CmsEditor mode="form" with a Collapsible JSON dump as children of the shell, not a bare Editor.Root + Form composition.

Props

PropRequiredDefaultDescription
clientyes—Top-level createCMS client (collection namespaces plus field sources).
collectionyes—Collection name on client.
rootIdyes—Document root.
branchIdyes—Branch to load and save.
schemayes—Editor schema.
componentsyes—Canvas block map.
templatesnoclient.templatesOverride for useCmsDocument template defaults.
modeno'canvas''canvas' or 'form'. Form skips Canvas.Root and EditorShell hides canvas chrome (palette, outline, inspector, device toggle).
classNameno—Applied to the page root, loading skeleton, and error state.
requireCommitMessagenofalseOpens the shell commit-message dialog on save.
childrenno—Canvas: after the shell. Form: inside the shell surface under FormSurface (demo extras such as a JSON Collapsible).

While developing the docs site locally, swap https://createcms.dev for http://localhost:4000.

When composing yourself

If you installed editor-app, you can stop here. CmsEditor already depends on editor-form, editor-canvas, and editor-shell. Install the items below only when you want to compose chrome yourself.

Field wrappers and cms controls

npx shadcn@latest add https://createcms.dev/r/editor-form.json

Canvas overlay chrome

npx shadcn@latest add https://createcms.dev/r/editor-canvas.json

Sidebar-grade shell

npx shadcn@latest add https://createcms.dev/r/editor-shell.json

Email split layout

npx shadcn@latest add https://createcms.dev/r/editor-email.json

Usage

Compose these parts inside <Editor.Root schema={…}>. Do not nest styled Field inside styled Form.

Cms field controls

import { Editor } from '@createcms/react/editor';
import { useCmsFieldSources } from '@createcms/react/editor/cms';
import {
  CmsSourcesProvider,
  cmsFields,
  Form,
} from '@/components/editor-form';

const sources = useCmsFieldSources(cmsClient);

<Editor.Root schema={schema} fields={cmsFields}>
  <CmsSourcesProvider sources={sources}>
    <Form blockId={blockId} />
  </CmsSourcesProvider>
</Editor.Root>

Styled container. Wrap the block's fields with the styled Form shell. Inner fields remain the headless primitives from @createcms/react unless you pass fields={cmsFields} (shadcn Input / Textarea / Select / Checkbox and CMS pickers). FormSurface is a padded selected-block form for CmsEditor mode="form" and the form-only demo. Do not replace those primitives with custom input chrome.

import { Editor } from '@createcms/react/editor';
import { Form } from '@/components/editor-form';

<Editor.Root schema={schema}>
  <Form blockId={blockId} />
</Editor.Root>

Styled parts mapped by the consumer. Render each field with the styled Field, FieldLabel, and FieldControl parts. Do not nest these inside Form.

import { Editor } from '@createcms/react/editor';
import { Field, FieldLabel, FieldControl } from '@/components/editor-form';

<Editor.Root schema={schema}>
  <Field blockId={blockId} name="title">
    <FieldLabel />
    <FieldControl />
  </Field>
</Editor.Root>

Shell and canvas. Place EditorShell (or its exported parts) and styled canvas parts inside Editor.Root. Pass Canvas.Root and overlay parts as children of EditorSurface. EditorShell wraps Canvas.Provider so palette items in the left sidebar can start drag sessions.

The items copy into your shadcn components directory. Adjust import paths to match your aliases.

editor-shell parts

The assembled EditorShell is a default composition of shadcn Sidebar (left palette + outline, right inspector), not a custom three-column grid. Cmd+B, icon-rail collapse, mobile sheet, and cookie persistence come from SidebarProvider.

ExportRoleComposed from
EditorProviderTooltipProvider + SidebarProvider + Canvas.Provider + keyboard (⌘S, ⌘K, undo/redo)shadcn sidebar, tooltip
EditorToolbarIcon buttons with Tooltip + Kbd, device ToggleGroup (canvas mode), save dirty-dot + spinnerbutton, tooltip, kbd, toggle-group, separator
EditorPaletteSidebarGroup of Canvas.PaletteItem rows with grip affordancesidebar, editor-canvas
EditorOutlineTree rows on SidebarMenuButton + Editor.OutlineItem, chevron collapse, type iconsidebar, collapsible
EditorInspectorTabs (Block / Page), empty state, sticky header with Duplicate / Deletetabs, editor-form
EditorSurfaceDevice-width canvas column, or a scrolling form column in mode="form"—
EditorShellDefault assembly. mode="form" hides palette, outline, inspector, and device toggles.all of the above

useEditorChrome() reads device preview, add-block dialog, and save-dialog state from EditorProvider.

Compose a custom layout from the same parts:

import {
  EditorInspector,
  EditorOutline,
  EditorPalette,
  EditorProvider,
  EditorSurface,
  EditorToolbar,
} from '@/components/editor-shell';
import {
  Sidebar,
  SidebarContent,
  SidebarInset,
  SidebarRail,
} from '@/components/ui/sidebar';

<EditorProvider>
  <Sidebar side="left" collapsible="icon">
    <SidebarContent>
      <EditorPalette />
      <EditorOutline />
    </SidebarContent>
    <SidebarRail />
  </Sidebar>
  <SidebarInset>
    <EditorToolbar />
    <EditorSurface>{/* Canvas.Root */}</EditorSurface>
  </SidebarInset>
  <Sidebar side="right" collapsible="icon">
    <SidebarContent>
      <EditorInspector />
    </SidebarContent>
    <SidebarRail />
  </Sidebar>
</EditorProvider>

editor-canvas parts

ExportRole
OverlayPortals overlay chrome into the canvas host
SelectionRingSelection outline with a block-type chip; motion respects reduced-motion
HoverRingHover outline
FieldRingFocused field outline
BlockToolbarIcon buttons + tooltips (drag, move, duplicate, delete) when no children
InsertButtonBetween-block insert control
DropIndicatorDrop line with endpoint caps; box variant for empty containers
DragHandleGrip handle for moving a block
PaletteItemDraggable palette row (used by EditorPalette)
DragPreviewPointer-following preview during a drag
InlineTextInline text editing glass