wagtail-visual-editor-bridge
v0.1.8
Published
Frontend side of wagtail-visual-editor: connects your headless preview with the Wagtail visual editor. Framework-agnostic (Next.js, Astro, ...).
Maintainers
Readme
wagtail-visual-editor-bridge
The frontend side of wagtail-visual-editor, a visual page editor for headless Wagtail.
It runs inside your preview page while the Wagtail admin shows it in an iframe, and:
- outlines the block under the mouse and shows its action bar: Edit, ↑, ↓, Duplicate, Delete, and a + on the top and bottom edges to add a block above or below;
- takes your site's colors through CSS variables (see Colors);
- sends each action to the admin, which edits the StreamField;
- blocks link navigation inside the preview;
- keeps the scroll position across refreshes and scrolls to the block that was just edited.
No framework required, no dependencies. Outside an iframe it does nothing, so it is safe to load.
Installation
npm install wagtail-visual-editor-bridgeUse the same minor version as the Python package (wagtail-visual-editor 0.1.x ↔ wagtail-visual-editor-bridge 0.1.x).
Usage
In your preview route, wrap each root block of the StreamField with its id. display: contents gives the wrapper no box of its own, so the layout doesn't change:
<div data-block-id="{block.id}" style="display: contents">
<!-- your block component -->
</div>Then start the bridge with the origin of your Wagtail admin. It only accepts messages from that origin:
import { startVisualEditBridge } from "wagtail-visual-editor-bridge";
const stop = startVisualEditBridge("https://cms.example.com");
// call stop() to remove itNext.js (App Router)
"use client";
import { useEffect } from "react";
import { startVisualEditBridge } from "wagtail-visual-editor-bridge";
export function VisualEditBridge({ adminOrigin }: { adminOrigin: string }) {
useEffect(() => startVisualEditBridge(adminOrigin), [adminOrigin]);
return null;
}Render <VisualEditBridge adminOrigin={...} /> only in the preview route.
Astro
---
const { adminOrigin } = Astro.props;
---
<div hidden data-admin-origin={adminOrigin}></div>
<script>
import { startVisualEditBridge } from "wagtail-visual-editor-bridge";
const origin = document.querySelector<HTMLElement>("[data-admin-origin]")?.dataset.adminOrigin;
if (origin) startVisualEditBridge(origin);
</script>API
function startVisualEditBridge(adminOrigin: string): () => void;Starts the bridge and returns a function that removes it (event listeners, outline and action bar).
Colors
The buttons and outline read CSS variables. Define them in the CSS of your /preview route; without them, the bar keeps its default colors.
| Variable | Default | Used for |
| --- | --- | --- |
| --wve-primary | #006df9 | Background of Edit, Move up/down and Duplicate |
| --wve-on-primary | #fff | Text and icon of those buttons |
| --wve-danger | #d1291e | Background of Delete |
| --wve-on-danger | #fff | Text and icon of Delete |
| --wve-outline | --wve-primary | Outline around the hovered block |
| --wve-insert | --wve-primary | Background of the "+" buttons |
| --wve-on-insert | --wve-on-primary | Icon of the "+" buttons |
/* CSS of your /preview route */
:root {
--wve-primary: #0f766e;
--wve-danger: #b91c1c;
}Buttons darken a little on hover and focus, whatever their color, so there are no hover variables.
Message protocol
Every message carries source: "wagtail-visual-editor".
| Direction | action | Payload |
| --- | --- | --- |
| preview → admin | ready | — |
| preview → admin | edit, moveUp, moveDown, duplicate, delete, insertBefore, insertAfter | id of the block |
| admin → preview | scrollTo | id of the block |
| admin → preview | labels | labels: {action: [text, title]} |
The action bar starts in English. When the preview loads, the admin sends the labels in the editor's language (labels message), so the bar follows the Wagtail admin language without any setup.
See the wagtail-visual-editor documentation for the backend setup, the block preview route and the iframe requirements.
License
MIT
