Visual block builder
From the plugin documentation · View source ↗
Open FlowEdit → Blocks in Admin2. Configuration read permission grants access to the library; configuration write permission is required to save and preview definitions. Page authors continue using the slash menu and instance form.
The visual block builder is included in FlowEdit 1.0.0-rc.1. For an end-to-end example, follow the landing-page walkthrough.
Find and open blocks
Search by label, ID, or keywords and filter by category and Active, Archived, or Disabled status. Cards show the block icon, field count, and Visual or Twig renderer label. Open a card’s title to edit it; its menu offers duplication and archive/restore. General plugin settings link back to this library.
Each editor has a bookmarkable route, for example
/admin/plugin/flow-edit#blocks/promo/layout (include your site’s base path).
The final segment can be fields, layout, or preview. Unsaved new definitions
exist only in the open editor until saved; bookmarking one does not persist it.
Create a reusable block
- Choose Create block, enter a label and stable ID, and pick blank, card, hero, or callout as the starting design.
- In Fields, add inputs, choose their types, labels, defaults and options, and reorder them. Image fields use FlowEdit’s existing media dialog, with page media, general media and configured stock providers. Colors use the same Huey controls as Admin2. Block settings offer a Lucide icon selector.
- In Layout, select an element in the tree to edit its bindings and style. Add elements at the root or inside a selected container. Columns contain two to four containers. Drag siblings or use their move buttons.
- In Preview, enter sample content and switch between desktop and mobile. Samples never become field defaults or page content. The active theme’s page template, CSS and block-template overrides render the preview. Optionally enter a page route to use its template, header and local media. Scripts and navigation remain disabled inside the isolated preview.
- Save block applies the definition to every page using it. Insert it with
/in the page editor and fill its fields as usual.
Layouts support containers, columns, headings, text, rich text, images, buttons,
repeating title/body items, and dividers. Styling includes spacing (0–96 px),
six-digit hexadecimal colors, alignment, and corner radius. Columns stack at
widths of 640 px or less. Limits: 256 types per collection, 64 fields per type, 128 elements
per layout and six nesting levels. One field can
occupy the shortcode body; lists use the existing [flow-item] format. Arbitrary
HTML/CSS/JavaScript and reusable block nesting are not supported.
Fields and bindings
Choose a field from the ordered list to edit its settings. Use as rich-text body makes a long-text field the block’s Markdown body. Repeating items already occupies that body, so it cannot coexist with a separate rich-text body. Use plain text fields for any additional descriptions on a repeating block.
| Element content | Compatible field |
|---|---|
| Heading, text, or button label | Non-content field other than Image |
| Rich text | The content field, excluding repeating items |
| Image source | Image field |
| Image alt text | Text or Long text stored as an attribute |
| Button destination | Link destination field |
| Repeating items | Repeating items field (up to 32 title/body entries) |
Plain content, image sources, alt text, and button destinations can also use literal values. Rich text and repeating items require a field binding. Style controls set literal layout values; an instance Color field does not dynamically bind to an element’s text/background style in this version.
Columns contain two to four Container children. Add another Container inside the selected Columns element to increase the count, then select that Container to add its content. The move controls and drag handles reorder siblings; they do not move an element between different parents.
Preview and media context
With no page route, preview uses the active theme’s default page template and
the block label as its page title. A supplied existing page route uses that
page’s header, template, and media; modular pages fall back to default.
The preview replaces that transient page’s body with the one block, so it does
not show surrounding page content or change the real page.
The preview page route is temporary and is not part of the saved definition. Use it when testing page-media filenames. Without page context, prefer General Media or a site-root image path. The shared picker retains normal permissions; configuration-only administrators may not have access to page/media actions. Alt text belongs in a separate block field, and layout sizing belongs in Layout.
Typing preview samples and rendering previews write no page content or block definitions. Uploading, cropping, or downloading stock media is a separate action that does create files. Cancelling the dialog or discarding a definition does not remove those files. Page-relative defaults only work on pages containing the referenced media; use a shared site path for a reusable default.
The iframe loads theme CSS, fonts, and images, while scripts, embedded frames, forms, and link navigation are disabled. Themes can override layout CSS; check contrast, spacing, and interactive elements again on the actual saved page.
Maintain definitions safely
- Duplicate creates a new type whose schema can change before its first save. A duplicated Twig block retains the original template path.
- Saved IDs, field names, types and body mappings are fixed. Labels, defaults, options, ordering and visual layout remain editable.
- Retire field hides the input while preserving stored values and rendering.
- Archive hides a type from new slash insertion. Existing instances remain editable and continue rendering. Restore reverses this. Save the change.
- Enabled retains its historical meaning: disabling also suppresses public output. Use Archive to stop new insertion without affecting existing pages. Disabled takes precedence when a type is both disabled and archived.
- There is no permanent deletion. Remove layout elements freely; retire saved fields instead of deleting them.
The environment selector chooses the configuration write target and is retained in the URL. It defaults to base configuration. Named targets must already exist; listing them requires system-read permission. Saves use ETags. Conflicts retain the draft and require an explicit reload/discard.
Save block is explicit; there is no draft/publish workflow for definitions. Inline errors prevent invalid definitions from saving. Failed saves retain the draft. On a conflict, record changes you need to keep, reload the latest collection using the retry flow, confirm discard, and reapply the changes. Do not repeatedly retry a stale definition expecting it to overwrite another tab.
Layout and label changes apply immediately to existing instances. A changed default initializes future inserts and supplies fallback values where applicable; it does not rewrite values already stored in page shortcodes. Archive/restore and field retirement also require saving. Leaving a dirty definition prompts for discard; switching its Fields/Layout/Preview views keeps the same draft. If you navigate while a save is pending, its response updates the library without replacing the next block’s draft. A failed background save appears as a notification.
Layout definition reference
Visual metadata lives on the block definition, never in page Markdown:
layout:
version: 1
nodes:
- kind: container
style: {padding: 24, gap: 16, radius: 12}
children:
- kind: heading
level: 2
value: {field: title}
- kind: richtext
value: {field: body}
This example assumes a title text field and a body textarea with
content: true. Node kinds are container, columns, heading, text,
richtext, image, button, items, and divider. Only containers and columns
have children. Headings support levels 2–6; the page template owns its H1.
Bindings use exactly one of {field: name} or {literal: text}. Main content
uses value; buttons use an additional url, and images an additional alt.
Supported style keys are integer padding, gap, and radius from 0 through
96; color and background as #rrggbb; and align as left, center, or
right. Other CSS properties and executable markup are rejected.
Integration
Definitions stay in plugins.flow-edit.custom_blocks; pages remain Markdown plus
shortcodes. Visual definitions add layout: {version: 1, nodes: [...]}. Existing
definitions without layout metadata use their original Twig paths. A matching
Twig template overrides a visual definition too; otherwise the standard renderer
handles it. See the storage contract.
POST /flow-edit/blocks/preview accepts a JSON block, values map and optional
page_route. It requires api.config.write; a nonempty route additionally
requires api.pages.read and that page’s read access. Requests are limited to
256 KB. It accepts visual and Twig definitions, validates them, and returns
data.document (the themed page), data.html (the block), data.css (visual
fallback styles), and data.theme (active theme name) without writing page files.
Missing pages/templates and invalid definitions produce validation errors.
values maps field names to strings; omitted values use field defaults.
Rich-text samples contain Markdown; repeating samples use the existing
[flow-item title="…"]…[/flow-item] string format. The renderer normalizes field
values and sanitizes rich text. The UI isolates the returned document in a
sandboxed iframe; API clients are responsible for isolating returned theme HTML.
Management uses GET/PATCH /config/plugins/flow-edit under the API base
(normally /api/v1). PATCH submits {custom_blocks: [...]} with the loaded
If-Match token and selected X-Config-Environment (default for the base
target). Existing API permission checks, config filtering, invalidation, and
ETag handling remain in effect. No alternate definition store is created.
Both page and field bundles ship with the plugin; installations need no Node.js.
Rebuild them after source changes using npm run build. Run the usual checks plus
php scripts/verify-builder.php for layout/configuration/rendering regressions.