Plugin Forge 2.6.0
Builder
The visual builder: lay out any model's content from elements, on the real page.
Builder is the platform's answer to Flatsome's UX Builder: a full-screen editor with the live page in the middle and a panel for adding, arranging and setting elements. It owns no content of its own. Any model becomes buildable with one trait and two columns, and any backend controller gets the editor with one behaviour — pages and posts in Content today, a product description tomorrow.
The plugin holds no element markup at all. Every element is a partial in the Forge theme, and the preview is the page itself: the builder switches preview mode on for a signed-in editor and re-renders through those same partials on every change. What an editor sees is exactly what the site will serve.
Every option is a named choice that maps to a theme token: "Tinted", not a colour; "Section", not a pixel value. An editor can rearrange a page freely and still cannot take it off the design system. The server cleans every tree before storing it.
What is inside
Elements
Defined in classes/Elements.php; rendered by themes/forge/partials/builder/. Another plugin adds one through the forge.builder.extendElements event.
- Container — stacked or in columns; width, spacing, background, alignment
- Title — H1 to H4, size independent of level, typed in place
- Text — paragraphs, lists, bold, italic, links, typed in place
- Image — from the media library, resized WebP, alt text and caption
For other plugins
How a model and its backend screen get the builder.
- Traits\HasBuilder — on the model, with content and draft columns
- Behaviors\BuilderController — on the backend controller: the builder/:id action and its save handler
- {% partial 'builder/render' nodes=… %} — on the theme page; preview mode needs nothing else
See it running
Every link below is this site, not a screenshot.
Changelog
[2.6.0] — 2026-10-22
Code only — no migration.
Added
BuilderModelcalls the extending plugin'sonBuilderLayoutPublished($tree)after a publish, and hasremoveBuilderLayout()to go back to the model's own content.
[2.5.0] — 2026-10-22
Code only — no migration.
Added
Behaviors\BuilderModel— the HasBuilder trait as a model behaviour, for a model that belongs to another plugin and is extended from outside. It keeps the layout inbuilder_content/builder_draft, leaves the model's own columns and validation alone, and takes its starting layout and preview page from two dynamic methods the extending plugin adds (builderStarterTree(),builderPageUrl()). Publishing means "this layout is live"; it does not publish the record. Forge.ShopBlocks uses it for product descriptions.
Changed
- The builder action renders the editor itself instead of through a
builder.phpview in the controller's folder, so a controller the behaviour is added to from outside needs no view. - The HTML permission check and the "Preview draft" link work with any model, not only ones
with
draft/content/url.
[2.4.0] — 2026-10-22
Code only — no migration.
Added
- Button element — the theme's own button, linking to a page, a product, a category, a
section (
#contact) or a phone/mail address: Main, Outline or Quiet; normal or large; left, centre, right or full width; this tab or a new one. The text is typed in place. Choose… next to the link searches the site's pages, posts, products and categories. - Background image for a container, with Darken, Darker or Lighten over it and a choice of which part stays in view. It is a real image behind the content — WebP in three sizes, loaded at once in a top section and lazily further down — not a CSS background. Darken switches the text to light, buttons included.
- Picker and link fields for any element. A
pickersearches a named source and keeps a chosen list (ordered, removable) or one item; alinkfield takes a path or address with Choose…. Sources are answered by plugins onforge.builder.search— this is how the shop's elements (Forge.ShopBlocks) choose products and categories without the builder knowing the shop.
Fixed
- A Main button inside a dark, accent, darkened-image or dark custom-colour section kept white text on what became a white button. Its text now turns dark there.
[2.3.0] — 2026-10-22
Code only — no migration.
Added
- Copy and paste, across pages. Copy on the element's toolbar and in its settings (or ⌘C when not typing) copies it with everything inside it; Paste in the Add panel (or ⌘V) puts it in on any page of the site. A copied section pasted while a section is selected goes after it, not inside. The copy lives in the clipboard and in the browser, so Paste works even where the clipboard is blocked.
- Copy as code / Insert code: the same copy as text —
{"forgeBuilder": 1, "nodes": [...]}— to carry to another Forge site, a message or a file. Pasted elements get new ids and go through the usual cleaning on save. - HTML element for embeds, forms and widgets, printed exactly as written. Scripts do not run in the builder's preview, only on the published page; embeds in the preview do not swallow the clicks that select them.
- ID and CSS classes on every element, under Advanced. The ID makes
/page#contactlinks work; classes are for the child theme'schild.css. - Custom size for titles and text — Size › Custom, in px, rem or em. A title of 32px or more shrinks on phones (to 60%, never under 24px); text keeps its exact size.
- Weight for titles and text: Light 300 to Extra bold 800, or the theme's default.
Security
- Raw HTML can run scripts on the live site, so it has its own permission, Write raw HTML. Without it the HTML element is not offered, and a save that adds or changes HTML code is refused; existing HTML blocks can still be kept and moved. Pasting an HTML block without the permission brings it in empty.
- IDs and classes are reduced to valid names, sizes to a number with px/rem/em within range, and weights to the listed values — anything else is dropped, never printed.
[2.2.0] — 2026-10-22
Code only — no migration.
Added
- Drag and drop in the page itself. The selected element's toolbar has a grip (with the element's name): drag it anywhere — between two titles, into another column or section, between sections. New elements drag straight from the Add panel into the page the same way; a click still adds where the panel says.
- While dragging, a blue line shows where it will land — horizontal between stacked elements, vertical between columns — and an empty container lights up as a whole. Near a section's top or bottom edge the drop goes beside it, as a new section, rather than inside; below the last section adds a new one at the end. The page scrolls when the pointer nears its top or bottom. Escape cancels.
- Drops follow the same rules as everything else: an element dropped between sections gets a container of its own, nesting stops at the usual depth, and a container cannot go inside itself.
Changed
- The preview frame's script and styles carry a version, so an updated builder reaches the browser at once instead of after a hard refresh.
- The Add panel no longer drags into Layers; it drags into the page. Layers still reorder by
dragging within the tree.
[2.1.0] — 2026-10-22
Code only — no migration.
Added
- Colour pickers. A container's background and a title's or text's colour offer the theme's own colours first, as swatches drawn in the active theme's real values (None, Surface, Tinted, Dark, Accent for backgrounds; Default, Muted, Accent for text), then a picker and a hex field for any other colour.
- Readable text on any background, automatically. Dark and Accent backgrounds switch the text tokens inside them to light, and a custom background gets a light or dark tone from its contrast, so default text, links and list markers stay readable without being set one by one.
- Margin and padding on every element, per side in px, with a link toggle for all four
and a Reset back to the theme's spacing. Values shrink on narrow screens to as little as
half, from full size at 1280px wide. Another plugin's element can opt out with
'spacing' => false. - Content / Style / Advanced tabs in the settings panel, as in Elementor. The open tab stays open from one element to the next.
builder_classes(node)andbuilder_style(node)for element partials (Classes\Look): theme colours as classes, custom colours and spacing as inline style.
Changed
- Style changes show at once. Layout, width, alignment, gap, spacing, size, colours, margin and padding apply in the preview on the same click or keystroke; the server's re-render follows 300ms after the last change to confirm. Previously every change waited for the round trip.
- The preview frame always loads fresh theme stylesheets, so an older copy in the browser's cache can no longer make a change look like it did nothing.
Security
- Colours are stored only as a known swatch or
#rrggbb; margin and padding only as whole numbers within range. Anything else falls back to the default.
[2.0.0] — 2026-10-22
The builder is now only the builder. Pages, posts and every other post type moved to
forge/content-plugin, which is built on this one. 1.0.0 had not been deployed anywhere, so
nothing needs carrying over.
Added
- Any model can be laid out in the builder. Add the
Forge\Builder\Traits\HasBuildertrait and two nullable long-text columns,contentanddraft; give its backend controller theBuilderControllerbehaviour and a$builderConfig(model class, noun, back and settings URLs). A model can override what a new record starts with, what publishing changes, its preview URL and its status line. A controller serving several kinds of record overridesbuilderFindModel()andbuilderGetConfig(). - The preview works on any front-end page. The builder switches preview mode on itself
(on
cms.page.init), for a signed-in backend user with the new Use the visual builder permission. The frame's script and styles are added for it, and its render requests are answered on the same page. A page only has to print{% partial 'builder/render' nodes=… %}; it needs no component from this plugin. builderTree()on the model returns the draft in the preview and the live content everywhere else, so a page component can hand it straight to the template.
Changed
- The builder's full-screen layout is referenced by path, so a controller in any plugin can use it.
- Preview renders go to the page itself with an
X-Forge-Builder: renderheader and a JSON body, instead of through a component's AJAX handler. Anyone without builder access gets the ordinary page back.
Removed
- The Pages and Posts menu, models, controllers and the
builderEntry/builderPostscomponents — nowcontentEntryandcontentListinforge/content-plugin. - The
forge_builder_entriestable (dropped bydrop_entries_table.php; replaced byforge_content_entries). - The Manage pages and Manage posts permissions, now one per post type in Content.
[1.0.0] — 2026-10-21
Added
- Pages and posts, laid out visually. A new Content menu in the backend with Pages and
Posts. A page lives at
/your-slug, a post at/blog/your-slugand on the/bloglist. Title, slug, status and search title/description are set on the settings form; a post adds a summary, a cover image and a publish date (a future date schedules it). - The builder — a full-screen editor in the style of Flatsome's UX Builder: the live page
in the middle, a panel on the left with Add, Layers and the selected element's settings.
- Four elements to start with: Container (stacked or two, three, four or uneven columns; reading, page or full width; spacing, background and column alignment, all named choices that map to theme tokens), Title (H1–H4, with size separate from level), Text (paragraphs, lists, bold, italic, links) and Image (from the media library, with alt text, caption and a choice of shapes).
- Titles and text are typed straight into the preview. Selecting text shows bold, italic, link and list buttons; ⌘B, ⌘I and ⌘K work too. Pasting always pastes plain text.
- Layers can be dragged to reorder or move between containers, and elements dragged in from the Add tab. The preview's toolbar moves, duplicates and deletes the selected element.
- Desktop, tablet and phone preview widths. Undo and redo (⌘Z, ⇧⌘Z), ⌘S to save, ⌘D to duplicate, Delete to remove, Escape to deselect.
- Save draft keeps changes out of the live page; Publish / Update makes them live. The status line says whether the live page is behind the draft, and leaving with unsaved changes asks first.
- The preview is the real page. The frame loads the page itself in preview mode and every
change is rendered by the page's own
builderEntrycomponent through the theme's partials, child overrides included. Nothing about an element's markup lives in the plugin. - The live markup carries nothing of the builder. The data attributes the editor needs are only printed in preview mode, which requires a signed-in backend user allowed to edit that kind of entry.
- Components:
builderEntry(one page or post by slug; 404 for anything unpublished) andbuilderPosts(published posts, newest first, paginated). - Permissions: Manage pages and Manage posts, under Content.
- New pages start with their title as the H1 and a paragraph under it; new posts with one paragraph, since the post template prints the title.
Security
- Every tree the editor sends is cleaned on the server before it is stored or rendered:
unknown elements and settings are dropped, every choice is checked against its options,
and text is reduced to an allowlist (
p,br,strong,em,a,ul,ol,li), with links limited to web, mail, phone, relative and fragment addresses. Image paths must stay inside the media library. - A page cannot take a slug a theme page already answers to (
/about,/shop, …): the theme's page would win the route and the entry could never be seen.