artisanpack-ui / visual-editor
A Gutenberg-powered visual editor for Laravel — post editor, site editor, and Blade/React/Vue block renderers.
Package info
github.com/ArtisanPack-UI/visual-editor
Language:TypeScript
pkg:composer/artisanpack-ui/visual-editor
Requires
- php: ^8.2
- artisanpack-ui/core: ^1.0
- artisanpack-ui/hooks: ^1.0
- illuminate/support: >=5.3
Requires (Dev)
- artisanpack-ui/cms-framework: ^2.0
- orchestra/testbench: ^10.2
- pestphp/pest: ^3.8
- pestphp/pest-plugin-laravel: ^3.1
Suggests
- artisanpack-ui/cms-framework: Required ^2.0 for the site editor + comments family — the `/visual-editor/site-editor` route is hard-coupled to cms-framework's templates, parts, patterns, global styles, and menus modules (plan 14), and the comments-family forks (#519) consume the Blog module's `Comment` model + `Post::comments` relation introduced in 2.1. Without it, the post-content editor still works but the site editor route returns an install gate and the comments-family blocks render against an empty data set.
- artisanpack-ui/media-library: Recommended ^1.2 — default media library integration. Wire MediaModal + uploadMedia through registerArtisanpackMediaBridge() in the editor bootstrap to power Gutenberg's media picker and upload flows. Not enforced by Composer; host apps are free to install a different library and register a custom bridge instead.
This package is auto-updated.
Last update: 2026-06-05 01:13:06 UTC
README
A Gutenberg-powered visual editor for Laravel applications. Brings the WordPress block editor to any Eloquent model, plus a full site editor for templates, template parts, global styles, navigation menus, and patterns.
V1.0 ships:
- Post editor — block-based content editing on any model that opts
in via the
HasBlockContenttrait. - Site editor — templates, template parts, theme.json-backed global
styles, navigation menus, and patterns; mounted at
/visual-editor/site. - 42 forked core blocks under the
artisanpack/*namespace.core/*markup pasted from upstream auto-converts on insert. - Three renderer packages — Blade (server-side), React, and Vue — for rendering saved block content on the public site.
- First-class pairing with
artisanpack-ui/cms-frameworkfor Posts, Pages, site-meta, navigation, and global-styles persistence. Both packages remain usable standalone.
Full documentation: see docs/. Start with Quick Start, or browse the documentation home for the full surface.
Installation
composer require artisanpack-ui/visual-editor php artisan migrate
For the site editor (recommended for most apps):
composer require artisanpack-ui/cms-framework php artisan migrate
See the Installation Guide and Quick Start for the full setup.
Version compatibility
The visual-editor and artisanpack-ui/cms-framework packages ship as a
version pair — both packages need to be present, and both need to be on
a compatible major version, for the site-editor integration to work.
Install one without the other and the site-editor's install gate (#432)
surfaces a "cms-framework required" page instead of mounting.
| visual-editor | cms-framework | Notes |
|---|---|---|
| v1.x | v1.x | Site-editor integration (this release) |
| v0.x | v0.x | Pre-v1 — no site-editor integration |
Bumping the major on either package without bumping the partner is unsupported.
Peer dependencies
The editor UI is built on @artisanpack-ui/react,
which is styled with DaisyUI and Tailwind CSS. Host applications
embedding the editor must have the following installed and loaded:
tailwindcss^4.0.0daisyui^5.0.0
Usage
Add the HasBlockContent trait to any model and register it in
config/artisanpack/visual-editor.php:
use ArtisanPackUI\VisualEditor\Concerns\HasBlockContent; class Post extends Model { use HasBlockContent; }
// config/artisanpack/visual-editor.php return [ 'resources' => [ 'posts' => App\Models\Post::class, ], ];
Mount the editor in a Blade view:
<x-visual-editor :model="$post" />
The site editor mounts automatically at /visual-editor/site when
cms-framework is installed and the configured SiteEditorAccessGate
permits the request.
See docs/post-editor/Blade-Component.md for the full
component reference and docs/site-editor.md for
the site-editor surface.
Documentation
Getting Started
- Installation Guide — Setup and configuration
- Quick Start — Ship your first post in under an hour
- Configuration — Full configuration reference
Core Concepts
| Topic | Doc |
|---|---|
HasBlockContent + resource map |
docs/content-model.md |
| Blade / React / Vue renderers | docs/renderers.md |
| Filters, actions, browser events | docs/Hooks-and-Events.md |
| Migration & WP import | docs/migration.md |
| Common problems | docs/troubleshooting.md |
Post Editor
| Topic | Doc |
|---|---|
| Surface tour | docs/post-editor.md |
| Quick start | docs/post-editor/Getting-Started.md |
<x-visual-editor /> reference |
docs/post-editor/Blade-Component.md |
| Embedding in Livewire | docs/post-editor/Livewire-Integration.md |
| Embedding in Inertia (React/Vue) | docs/post-editor/Inertia-Integration.md |
| Theming editor chrome | docs/post-editor/Theming.md |
Site Editor
| Topic | Doc |
|---|---|
| Surface tour | docs/site-editor.md |
| Quick start | docs/site-editor/Getting-Started.md |
SiteEditorAccessGate contract |
docs/site-editor/Access-Gate.md |
| Template hierarchy + parts | docs/site-editor/Templates.md |
| theme.json-backed global styles | docs/site-editor/Global-Styles.md |
| Menus, locations, fallbacks | docs/site-editor/Navigation.md |
| Synced vs unsynced patterns | docs/site-editor/Patterns.md |
Blocks
| Topic | Doc |
|---|---|
| Block library overview | docs/blocks.md |
| Authoring custom blocks | docs/blocks/Custom-Blocks.md |
| Per-breakpoint values | docs/blocks/Responsive-Design-Tools.md |
| Per-state overrides | docs/blocks/State-Design-Tools.md |
Developer Resources
- Developer Guide — Extending the editor
- Hooks and Events — Filters, actions, and browser events
Gutenberg adoption — transient shims
The V1 editor adopts the upstream @wordpress/* packages. Some of those
packages expect a WordPress backend that this package doesn't provide,
so we ship temporary shims under resources/js/visual-editor/vendor/:
core-data-shim.ts— aliased invite.config.tsas@wordpress/core-data. Provides the entity registrations and selectors Gutenberg expects (getEntityRecord,getEntityRecords, resolvers, permissions stubs). Templates, template parts, navigation, patterns, global styles, attachments, and site-meta are wired through to the package's REST surface.media-upload-stub.tsx— registerseditor.MediaUploadvia@wordpress/hooksso the media-library picker oncore/imageis routed through a stub or the realregisterArtisanpackMediaBridge(MediaModal, uploadMedia)bridge.
Both shims will be replaced over the V1.x release line as the cms-framework
side surfaces real backings. Every selector or filter implemented here is
one to re-verify against Gutenberg upgrades. See
docs/troubleshooting.md.
Block defaults
V1 ships a frozen allow-list of forked blocks under the artisanpack/*
namespace. The defaults in config/artisanpack/visual-editor.php expose every block
that landed during the Phase I block fork — @wordpress/block-library's
registerCoreBlocks() is no longer called, and the editor registers
only the in-package forks discovered under
resources/js/visual-editor/blocks/. See docs/blocks.md
for the full block library overview and the core/* → artisanpack/*
mapping.
The forked allow-list covers the content, media, layout, widget, entity,
loop/feed, comments, query/pagination, and authentication clusters.
Entity blocks (artisanpack/post-*, artisanpack/site-*,
artisanpack/template-part, artisanpack/navigation) and the loop /
feed cluster (artisanpack/query, artisanpack/post-template,
artisanpack/archives, artisanpack/categories,
artisanpack/tag-cloud) need an entity in scope to render meaningful
content — pair the editor with
artisanpack-ui/cms-framework
and they resolve against Posts / Pages / templates / site settings
end-to-end. Standalone, they fall back to empty shells rather than
crashing.
disabled_blocks is empty by default: with the I7 cutover (#415) the
editor no longer registers any core/* block, so there's nothing to
deny-list. New @wordpress/block-library releases similarly bring no
new registrations into this package — additions land only when a fork
is added to the in-package blocks directory and to the allow-list.
from:core/* transforms ship on each fork so existing core/* markup
pasted from upstream converts on insert.
Override the defaults by publishing the config to
config/artisanpack/visual-editor.php and editing the enabled_blocks
/ disabled_blocks arrays. The deny-list always wins over the allow-list.
Using with cms-framework
The visual editor is fully usable standalone, but pairs with
artisanpack-ui/cms-framework
to unlock:
- Editable
PostandPagecontent out of the box. - A real backing for
core/site-*blocks (site title, tagline, logo, icon). - Working
core/post-*,core/query, taxonomy widget, and navigation blocks. - Templates, template parts, patterns, global styles, and menus persisted via cms-framework's models.
- Seeded
visual_editor.*permissions.
composer require artisanpack-ui/visual-editor artisanpack-ui/cms-framework php artisan migrate
Both packages are loosely coupled — cms-framework's editor wiring is
guarded by class_exists(\ArtisanPackUI\VisualEditor\VisualEditor::class),
so each remains usable on its own. See
docs/site-editor/Getting-Started.md
for the site-editor pairing walkthrough.
Extensibility
ap.visual-editor.resources
Register slug → Eloquent model class mappings used by
/visual-editor/api/{resource}/{id}/content. Filter contributions are
merged with config('artisanpack.visual-editor.resources'); the static
config wins on key collision so host-app overrides always take
precedence. Models must use
ArtisanPackUI\VisualEditor\Concerns\HasBlockContent — invalid entries
surface as InvalidArgumentException on first request rather than at
boot, so a contributor's standalone install never trips host boot.
addFilter('ap.visual-editor.resources', function (array $resources): array { return array_merge([ 'posts' => App\Models\Post::class, ], $resources); });
Full contract: docs/content-model.md and
docs/Hooks-and-Events.md.
ap.icons.register-icon-sets
Editor chrome icons resolve through artisanpack-ui/icons. Register
additional icon sets in a service provider — see the
artisanpack-ui/icons docs.
i18n
Editor strings use @wordpress/i18n with the artisanpack-visual-editor
text domain. The domain is initialized via bootI18n() in
resources/js/visual-editor/vendor/i18n.ts.
Regenerate the placeholder .pot catalog with:
npm run i18n:extract
The extractor scans resources/js/visual-editor/**/*.{ts,tsx} for
__/_x/_n/_nx calls bound to the text domain and writes
languages/artisanpack-visual-editor.pot.
Contributing
As an open source project, this package is open to contributions from anyone. Please read through the contributing guidelines to learn more about how you can contribute to this project.