ArtisanPack UI
DocsVisual EditorPost editor

Post editor

Visual Editor v1.18.0

The post editor is the surface authors use to edit a single resource (post, page, custom CPT). It's mounted via the Blade component and built on top of ...

The post editor is the surface authors use to edit a single resource (post, page, custom CPT). It's mounted via the <x-visual-editor /> Blade component and built on top of @wordpress/block-editor, with chrome restyled to DaisyUI through @artisanpack-ui/react.

This page is a tour of the surface: what's where, how to extend each region, what API endpoints back it.


1. Layout

┌───────────────────────────────────────────────────────────────────────┐
│  Topbar: Title · Status · Preview · Save · Inserter toggle           │
├──────────────┬──────────────────────────────────────┬─────────────────┤
│              │                                      │                 │
│   Block      │            Canvas                    │   Inspector     │
│   library    │     (iframe-wrapped editor)          │   sidebar       │
│   sidebar    │                                      │                 │
│              │                                      │                 │
├──────────────┴──────────────────────────────────────┴─────────────────┤
│  Footer: breadcrumbs, autosave indicator, block count                │
└───────────────────────────────────────────────────────────────────────┘
  • Topbar — document chrome (title, status, preview, save, inserter toggle).
  • Block library sidebar (left, collapsible) — browsable list of all registered blocks, grouped by category. Also surfaces patterns.
  • Canvas (center) — iframe-wrapped block editor. Isolated styles so the front-end theme renders inside without leaking out.
  • Inspector sidebar (right) — block-level controls (alignment, color, typography, spacing, advanced).
  • Footer — breadcrumbs through the block tree, autosave status, block count.

2. Canvas

The canvas is an iframe-wrapped BlockEditorProvider from @wordpress/block-editor. Iframe isolation gives blocks access to the theme's typography and color CSS without leaking editor chrome styles into the content.

Theme CSS injection is driven by the active global-styles record (see Global styles). The CSS is fetched from GET /visual-editor/api/global-styles/lookup → GET /global-styles/{id} on boot and injected into the iframe via the React renderer's <GlobalStyles> component.

Block selection, drag-and-drop, undo/redo, and keyboard shortcuts come from @wordpress/block-editor unchanged.

The topbar's View with template switch composes the resolved template's header and footer around the canvas as a read-only preview, so authors can judge their content in context without leaving the editor. Template chrome is edited in the site editor, reached from the composed canvas's Edit template ↗ deep link. See Composed view.


3. Inspector sidebar

The right sidebar renders InspectorControls for the currently selected block. Every forked artisanpack/* block ships a complete control set:

  • Alignment + dimensions
  • Color (text, background, link, gradient)
  • Typography (font family, size, line height, letter spacing)
  • Spacing (margin, padding, block gap)
  • Border (radius, style, width, color)
  • Advanced (HTML anchor, additional CSS class)

Block authors add their own controls inside <InspectorControls> from their edit.tsx — see Custom blocks §5.

The sidebar is also where the document-settings panel renders (status, slug, excerpt, featured image, author, comments). Visibility is controlled by the supports attribute on <x-visual-editor /> — fields you don't want shown can be turned off without code changes.


4. Block library sidebar

Opens with the topbar inserter button. Lists every block returned by GET /visual-editor/api/blocks, grouped by category (from block.json) and filtered by the enabled-block allow/deny lists.

The same panel also surfaces patterns — both synced (referenced from the pattern store) and unsynced (template snippets to drop inline). See Patterns.

Slash command (typed inline in any rich-text block) routes through the same registry — /heading, /image, /pattern hero, etc.


5. Media library integration

Image, cover, video, audio, file, and post-featured-image blocks all route media picking through Gutenberg's editor.MediaUpload hook.

The package ships two implementations:

  • Stub (media-bridge/media-upload-stub.tsx) — registered by default. Shows a placeholder picker that does nothing. Useful in the dev sandbox and in apps that don't have a media library yet.
  • Real bridge — host apps that ship artisanpack-ui/media-library (or any compatible store) call registerArtisanpackMediaBridge(MediaModal, uploadMedia) from @artisanpack-ui/visual-editor before mounting the editor. The bridge swaps the stub for a MediaModal that opens the host's picker UI.
import { registerArtisanpackMediaBridge } from '@artisanpack-ui/visual-editor';
import { MediaModal, uploadMedia } from '@artisanpack-ui/media-library';

registerArtisanpackMediaBridge(MediaModal, uploadMedia);

Selected media is fetched server-side from GET /visual-editor/api/attachments/{id} and returned in the WordPress-shape Attachment object Gutenberg blocks expect (id, source_url, alt_text, media_details).

The active bridge slug is recorded in config('artisanpack.visual-editor.media.bridge') (default 'artisanpack-ui/media-library'); the server-side adapter that converts host media records into WP-shape attachments is set in config('artisanpack.visual-editor.media.adapter') (default: GutenbergAttachmentAdapter). Swap the adapter to integrate with a different media store.


6. Document settings panel

Inside the inspector sidebar, the document panel exposes:

Field Source supports key
Title :initialTitle always shown
Slug :initialSlug always shown
Status :initialStatus always shown
Excerpt :initialExcerpt excerpt
Featured image :initialFeaturedImage featuredImage
Author :initialAuthorId + :authorOptions always shown
Comments :initialCommentsOpen comments

Pass supports = ['excerpt' => true, 'featuredImage' => false, 'comments' => true] to opt fields in or out — see Blade component reference.

Document-field changes write back to the model via the same content endpoint, batched with block changes.

Since v1.15.3 (#992), Post Title, Post Excerpt and Post Content blocks in the canvas show the entry being edited, for posts, pages and custom content types. Title and excerpt are editable inline and stay in sync with the Title and Excerpt fields above; Post Content shows a read-only preview of the canvas. For custom content types without onMetadataChange, title and excerpt are saved through PUT /visual-editor/api/{resource}/{id}/entry (see Content model).


7. Autosave + save flow

  1. Author edits a block.
  2. After ~1s of idle, the editor dispatches ve:editor:change on window.
  3. The autosave request fires: PUT /visual-editor/api/{resource}/{id}/content.
  4. On 200, the editor dispatches ve:editor:autosave with { resource, id, blocks, updatedAt }.
  5. ⌘S or topbar Save bypasses the debounce and dispatches ve:editor:save with the same payload.

See Livewire integration for the pattern of bridging these events back to a server-rendered shell.


8. Keyboard shortcuts

Standard Gutenberg shortcuts: ⌘B/⌘I/⌘U, ⌘K (link), ⌘⇧K (unlink), ⌘Z/⌘⇧Z (undo/redo), ⌘⌥M (insert media), ⌘S (save), / (slash inserter), ?? (open shortcut help modal).

The shortcut help modal is restyled with @artisanpack-ui/react's Modal so it picks up the host theme.


9. Embedding

The post editor is the same surface whether mounted via Blade, Livewire, or Inertia. Embedding recipes:


See also