Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Visual Editor Laravel Package

statamic-addon/visual-editor

A Statamic addon that adds a visual, in-context editing experience, letting editors work with page content directly on the front end instead of switching to the control panel. Install via Composer and enable it for the fields or templates you want editable.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Statamic addon that makes Live Preview editable: you annotate template markup with a visual_edit tag, and clicking an element inside the preview jumps the Control Panel to the field that renders it. Everything else in the package (the overlay, the postMessage bridge, the CP panels, the dock) exists to serve that one gesture.

Install it like any Statamic addon and open an entry in Live Preview before touching a single template — you need to see the unannotated baseline first.

composer require statamic-addon/visual-editor

Where to look first, in order:

  1. The two skill files (skills/visual-editor-antlers/SKILL.md, skills/visual-editor-blade/SKILL.md) — these are the actual user-facing documentation. There is no meaningful README; treat them as the spec.
  2. One Replicator set partial in your own project. Not a global, not a Bard field, not the entry title — a single set partial with a clean outermost element.

First use case — the 5-minute win:

{{# resources/views/partials/sets/text.antlers.html #}}
<section class="prose" {{ visual_edit }}>
    <h2>{{ heading }}</h2>
    {{ text }}
</section>

Open the entry in Live Preview, hover the section, click it. The CP should focus the heading/text fields of that specific set. Once that works, replicate it across your sets and you have covered the majority of author-facing editing in an afternoon.

Confirm the zero-footprint claim immediately. Load the published URL (not Live Preview) and inspect the markup — there must be no data-sid, no data-sve-*, nothing. If you see attributes in production output, stop and diagnose before annotating more templates, because you now have an SEO/markup problem across the site.

Implementation Patterns

Set targeting is implicit; field targeting is explicit. This is the mental model to internalise. For Replicator/Bard/Grid, {{ visual_edit }} reads _visual_id and type out of the Antlers context by itself. For everything else — title, SEO fields, group sub-fields — you must name the field:

<h1 {{ visual_edit field="title" }}>{{ title }}</h1>
<p {{ visual_edit field="page_info.author" }}>{{ page_info:author }}</p>

Annotate at loop granularity, not template granularity. The right pattern for grids and Bard sets is inside the loop so each row carries its own identity:

{{ links }}
    <li {{ visual_edit }}>
        <a href="{{ link_url }}">{{ label }}</a>
    </li>
{{ /links }}

One tag per rendered item, on the outermost element of that item. Multiple tags in a single file is normal and correct. What is not correct is tagging both a parent element and a child element for the same set — only one is needed, and doubling up gives you an ambiguous target.

Use the pair tag when the markup has no single root. It wraps your content in a <div>, so only reach for it when you can't restructure around an existing element:

{{ visual_edit }}
    <h1>{{ hero_title }}</h1>
    <p>{{ hero_text }}</p>
{{ /visual_edit }}

Blade requires the unescaped syntax and an explicit context. This is where most Blade adopters lose ten minutes:

<section class="fluid-grid" {!! Statamic::tag('visual_edit')->context($set->all())->fetch() !!}>
    {{-- content --}}
</section>

<h1 {!! Statamic::tag('visual_edit')->blueprint('collections.pages')->field('hero_title')->fetch() !!}>
    {{ $hero_title }}
</h1>

->context($set->all()) is the Blade equivalent of Antlers' automatic context — without it, set targeting has nothing to read. ->blueprint() accepts collections.{handle} or globals.{handle} and only affects the label shown in the tooltip; CP navigation works without it, so it's the last thing to add, not the first.

Surface invisible fields behind a preview guard. SEO metadata and similar have no natural element. Annotate a representative placeholder that only exists during preview:

{{ if live_preview }}
    <div {{ visual_edit field="seo_title" outline-inside="true" }}>{{ seo_title }}</div>
{{ /if }}
@if(request()->isLivePreview())
    {{-- annotated placeholder --}}
@endif

This is the highest-leverage pattern in the whole addon for editor experience — it turns "where do I edit the meta description" into a click. Do it once in a shared SEO component or partial rather than per-page.

outline-inside="true" is a layout fix, not a style choice. Dense grids and tight cards have outbound outlines that overlap neighbours and make the target ambiguous. Reach for it when hovering the wrong element.

The preview "+" button is not the addon's own picker. Under a block in Live Preview, the plus opens Statamic's native Search Sets picker — the same one as sidebar Add Set, with the same Headline/Richtext/Links sets. Your set definitions and grouping are therefore the source of truth; there is nothing extra to configure per set.

Gotchas and Tips

The dist is load-bearing and fragile — never hand-edit it. addon.js imports overlay-host-XXXX.js by exact hashed filename. A Vite build of a single entry rewrites the hash, orphans the old file, and the Control Panel loads nothing: the entire editor vanishes with no console error pointing at the cause. Rules that follow:

  • Never edit manifest.json hashes by hand.
  • Never vendor:publish this package's dist into public/vendor/ — the CP uses a symlink and the preview fetches /!/sve/build/…, so publishing actively breaks it.
  • Never delete overlay-host-*.js, addon-*.js, bridge-*.js, preview-*.js from resources/dist/build/assets.
  • After any dist change, run node scripts/assert-dist-integrity.mjs. If it fails, restore from resources/dist/locked or git — do not "rebuild a bit".

If the overlay doesn't appear at all, check the /!/sve/build/… asset requests in the network tab before debugging your annotation. A 404 there means the editor is gone regardless of template correctness. This is by far the most common "nothing happens" cause.

If the overlay appears but clicking selects the wrong field, inspect the element inside the preview iframe (not the parent page) for the data-sid / data-sve-* attributes. Missing attributes mean the tag didn't run — you're outside Live Preview, or the partial you edited isn't the partial being rendered (cached view, or a set rendered inline elsewhere in a larger template).

Antlers colons, tag dots. {{ page_info:author }} renders the value; visual_edit field="page_info.author" targets it. Mixing them silently produces a non-functional target rather than an error.

Blade {{ }} will quietly ruin you. It HTML-escapes the attribute string into visible text on the page. Always {!! !!}. This is the single most common Blade mistake and it renders as garbage, not an error, outside Live Preview.

Live Preview is the only place this exists. The tag emits nothing otherwise by design. That means: no production footprint (great — annotation work is cheap to add, cheap to revert, and safe to ship to a client site), but also no way to validate annotations outside preview. Build a short QA pass into your workflow: open each annotated template type once in Live Preview and click every set.

Respect the fieldtype boundary. The addon's own rules forbid subclassing or replacing Statamic's built-in fieldtypes (replicator, bard, grid, text, iconify), wrapping their Vue components, or appendConfigFields on the Fieldtype base class. If you're tempted to "just add a setting" to every field, do it on one fieldtype. Violating this breaks the field settings panes across the whole CP, and the symptom looks unrelated to your change.

Maturity and vendor risk are real. One star, zero dependents, a single maintainer, and documentation that ships as agent instruction files rather than a README. The Blade ->params() reference in the available docs is even truncated mid-example. Before committing broadly:

  • Pin an exact version in composer.lock; the release chain (cp:build → commit → tag → composer) is manual, so untagged breakage is plausible.
  • Read lib/protocol.js and the tag class directly rather than trusting the docs for parameter names.
  • Because annotations are zero-footprint, your exit cost is low — worst case you strip the tags and lose the click-to-edit affordance, not content. Annotate widely, but keep the tags trivially removable (never rely on visual_edit output for layout or JS hooks).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console