artisanpack-ui/visual-editor
A visual editor for Laravel that lets you build and edit page content directly in the browser, with a drag-and-drop interface that saves structured data your Blade views and models can render.
This release (v1.15.2) is additive and bug-fix heavy — no breaking changes are called out — so the shortest path is: install, publish config/assets, point the editor at a model that stores block JSON (a blocks column or a dedicated blocks table), and render that JSON through the Blade renderer on the front end. Confirm the exact install command and entry point against the package README; the notes reference artisanpack/* block names and three renderers (Blade, React, Vue), which tells you the integration surface.
Where to look first, in order:
QueryInliner. It is the heart of dynamic content: it resolves Query loops on the front end and stamps _resolvedTotal, _resolvedItems, _resolvedCurrentPage, _resolvedPerPage, _resolvedTotalPages onto the block. Everything about Query Total, Query Pagination, Post Template, Post Reading Time and Related Posts flows through it.mobile: / tablet: keywords, and the Navigation collapse point now references registry widths (767px / 1023px by default). Understanding this saves hours of "why did my nav collapse at 600px" debugging.First use case: a CMS-backed "blog index" page — a Query loop with a Post Template grid, a Query Total above it, Query Pagination below it, and Post Reading Time inside each item. This exercises the newest blocks and the trickiest subsystem (query resolution) in one screen.
[
{ "name": "artisanpack/query-total", "attrs": { "displayType": "range-display" } },
{ "name": "core/query", "attrs": { "queryId": "blog-index" } },
{ "name": "artisanpack/query-pagination", "attrs": {} },
{ "name": "artisanpack/post-reading-time", "attrs": { "suffix": " min read" } }
]
(The above is illustrative of the attribute names this release documents, not a verbatim fixture.)
Blade-first, React/Vue only where you need them. All three renderers are kept in parity, but the notes repeatedly call out blocks the React and Vue renderers don't render (artisanpack/form). If you're on Inertia, keep a Blade fallback path for those blocks rather than assuming parity.
Query loops as the primary dynamic pattern. The QueryInliner resolves loops on the front end and stamps _resolved* attributes. Build your own blocks or partials against those stamped values rather than re-running WP_Query-equivalent lookups — that's how Query Total, Query Pagination and Related Posts stay consistent. Each loop now pages on its own queryId query var, so multiple loops on one page (e.g. a sidebar "related" loop plus a main archive loop) no longer fight over pagination.
Compose blocks rather than custom-coding layouts. Post Template grids now collapse to two columns at ≤1023px and one at ≤767px, matching the Grid block. Related Posts now reads displayLayout (list or grid with columns) and Block spacing. If you were hand-rolling responsive grids in Blade partials, replace them with these blocks and let the renderer own the media queries.
Style through block supports, not custom CSS. Query Total, Image, Table, Post Featured Image and others carry typography, colour, spacing and border supports. Setting these in the editor keeps output portable across renderers; hardcoded CSS in your theme will diverge from the canvas preview.
Forms: lean on layout="inline". The Form block emits data-layout="inline" and an is-layout-inline class so your host form island and theme can put the submit button on the same row. Style against those hooks instead of reaching into the block internals.
Progressive enhancement for Social Share. The copy-link chip is a plain anchor by default and is upgraded to a clipboard button by the interactivity script, announcing results in a polite live region. Treat the anchor as the source of truth; don't build your own clipboard handler on top.
Cache deliberately. Because totals and pagination are resolved at render time, any full-page cache must key on the query vars each loop uses. Caching a Query Total page without varying on page number will serve "1–9 of 25" to everyone.
Navigation collapse as a design token. Set collapseBreakpoint to mobile or tablet rather than writing your own media query. The renderers emit <style data-ve-navigation-collapse> once per page with collapses-at-{key} on the <nav>; overriding it in your theme means fighting a page-level style tag.
defaultOpen needs a real boolean. An Accordion panel opens in Blade only for true; React and Vue match. A string "true" from loosely-typed JSON or a form post will silently stay closed._resolved* first.collapseBreakpoint keeps the legacy 600px. 600px matches none of the registry widths, which is exactly the bug this attribute fixes. Set it explicitly on any nav in a wide header or you'll still overflow between 600px and 767px/1023px.status is now dropped on the front end. If any internal tooling relied on a draft/private status persisting into front-end loop resolution, it won't. This is the intended security fix (cms-framework #403) — don't "restore" it with a filter.F j, Y. Audit existing content where authors set a format and were unknowingly ignored; those pages will change appearance after upgrade. Carbon translatedFormat() is used server-side, with a PHP-format port in React/Vue — locale-sensitive formats may differ subtly between renderers.width / height are strings now. "auto", "30px", "50%" print as inline style plus is-resized, matching the editor's save(). Legacy numeric values still render as attributes. If you were casting these to int in a custom renderer, stop..post-terms a will no longer match unlinked terms; use the wrapper element.<nav> used to contain a lone "1". If you had CSS targeting that nav for spacing, it will now collapse entirely — use :empty checks or a wrapper.colspan, rowspan, scope and has-fixed-layout. Existing CSS with hardcoded table-layout: fixed may now double up with the block's fixed-layout class.layout on Form accepts only stacked or inline. Any other value is dropped silently. Stacked output is unchanged, so no migration is needed for existing forms.mobile:ap-basis-auto beats ap-basis-[280px]. This fixes a class of "my responsive override does nothing" bugs, but the specificity dance still exists — keep arbitrary values out of base declarations you intend to override responsively.!important) to undo .is-layout-flex { flex-wrap: wrap; align-items: center }, remove them; they'll now over-apply.data-ve-navigation-collapse, data-layout, collapses-at-*, is-resized and _resolved*; check whether the block is one of the Blade-only ones when debugging an Inertia page.QueryInliner hook is the place to adjust query vars if queryId-per-loop isn't granular enough for your setup.How can I help you explore Laravel packages today?