Skip to main content

Capell uses essential storage for sessions, security, and interface preferences. Analytics and marketing storage stay off unless you accept them.

Cookie Policy

Layout Builder

Layout Builder gives Capell teams reusable layouts, named areas, approved widgets, content-first editing, and public-safe render graphs.

Overview

What Layout Builder provides

Layout Builder gives Capell teams reusable layouts, named areas, approved widgets, content-first editing, and public-safe render graphs.

It now adds reviewed bulk layout changes for the maintenance work that usually becomes risky at scale.

Admins can define criteria, preview every affected layout and page, inspect before and after widget order, then approve the stored run.

Hash-guarded apply skips layouts that changed after preview, while page-scoped widget assets follow moved widgets.

Use it when a Capell site needs editor flexibility and maintainable shared layout control instead of page-by-page rebuilds.

/ 06
6 captures Visual gallery
Install

Install on your Capell site in three steps.

Each extension ships as a standard Composer package. Purchases unlock account access immediately before you install.

Support
First-party Capell support for active marketplace customers.
Data access
Reads and writes Capell content structures required by the package; public output receives rendered content only.
  1. 01

    Require the package

    composer require capell-app/layout-builder
  2. 02

    Register the extension

    php artisan capell:extension:enable capell-app-layout-builder
  3. 03

    Clear schema cache

    php artisan capell:admin:clear-schemas-cache
Works well with this

Related extensions

Browse the packages that usually pair with Layout Builder or buy the suite it belongs to.

Included in suites
Content launch

Editorial Foundation Suite

$0.00

Start a Capell site with Layout Builder, reusable blocks, Foundation Theme, navigation, Media Library, and blog publishing in one free content launch path.

Layout Builder Block Library Foundation Theme Navigation Media Library Blog
Package README Install impact, package shape, common pitfalls, and maintenance notes. Open

Capell Layout Builder

capell-app/layout-builder owns Capell's visual layout composition layer: layout containers, widgets, widget assets, public layout graphs, content-first editing, and the Filament layout editor.

Core still owns sites, pages, languages, URLs, themes, and base content models. Admin still owns the Filament panel shell. Layout Builder plugs into both through package registrars and exposes its public API from the Capell\LayoutBuilder namespace.

Why It Helps Your Capell Workflow

  • Provides the visual composition layer for Capell: layouts, containers, widgets, assets, public render graphs, and editor mutations.
  • Helps editors assemble pages without storing theme-specific presentation markup in database content fields.
  • Gives developers Actions and registries for public-safe layout payloads, reusable presets, layout areas, and content-first editing.
  • Lets admins make reviewed bulk layout changes across many layouts, with a stored preview, exact per-layout diffs, page impact counts, warnings, approval, and hash-guarded apply.

Best Used With

Install

composer require capell-app/layout-builder
php artisan capell:layout-builder-install

The install command publishes and runs the package migrations listed by Capell\LayoutBuilder\Support\CapellLayoutBuilderManager.

Configuration

Configuration lives in config/capell-layout-builder.php.

Key Purpose
editor_mode.default Default editor mode. Defaults to content_first.
editor_mode.allowed Allowed modes. Current values are content_first and layout_first.
preview.match_frontend_container_layout Match admin preview container layout to frontend columns.

The visual editor preview uses breakpoint-aware canvas width variables and keeps save controls in a sticky preview action bar so desktop, tablet, and mobile frames stay usable inside narrower admin panels. | widget.skip_render_empty | Skip empty widgets in public rendering. | | default_widget | Default renderable key for new widgets. |

Main Surfaces

Surface Package path
Public graph building src/Actions/BuildPublicLayoutGraphAction.php
Public widget payloads src/Contracts/PublicWidgetPayloadContributor.php, src/Support/DefaultPublicWidgetPayloadResolver.php
Layout areas src/Support/LayoutAreas/LayoutAreaRegistry.php, src/Actions/ResolveLayoutAreaContainersAction.php
Widget presentation projection src/Actions/ResolveWidgetPresentationDataAction.php
Layout health checks src/Actions/AnalyzeLayoutHealthAction.php
Reusable layout presets src/Models/LayoutPreset.php, src/Actions/SaveLayoutPresetAction.php, src/Actions/ApplyLayoutPresetAction.php
Content-first inventory src/Actions/BuildLayoutContentInventoryAction.php
Layout mutations src/Actions/Mutations/
Bulk layout changes src/Actions/BulkChanges/, src/Console/Commands/LayoutBulkChangeCommand.php
Filament resources and schemas src/Filament/
Livewire editor src/Livewire/Filament/LayoutBuilder.php
Admin views and components resources/views/

Bulk Layout Changes With Review Approval

Large Capell sites often need a layout change that is simple in one page and risky across dozens of shared layouts: move breadcrumbs below the hero, remove a widget, swap two widgets, or move a widget from a sidebar container into the main container.

Layout Builder handles that as a reviewed mutation workflow instead of a one-off script:

  1. Define criteria and a guided operation.
  2. Store a preview run with the exact target layouts, page counts, before and after container state, structured diffs, warnings, skipped records, and hashes.
  3. Review the preview in the Filament action or from the Artisan command.
  4. Approve the stored run when the result is correct.
  5. Apply with hash guards so layouts changed after preview are skipped as drifted instead of overwritten.

The first typed operations are move_widget, remove_widget, swap_widgets, and move_widget_to_container. Criteria can filter by site, theme, group, layout key, active state, and required widget. Specific widget occurrences can be targeted when a repeated widget appears more than once.

Page-scoped widget_assets follow moved widgets when container or occurrence changes. Remove operations can warn about page-scoped assets or delete them explicitly. Default widget assets are not rewritten globally; ambiguous default asset moves are blocked so editors do not accidentally change shared content assignments.

The same Actions power both surfaces:

php artisan capell:layouts:bulk-change --spec=/path/change.json --preview
php artisan capell:layouts:bulk-change --approve=run-uuid
php artisan capell:layouts:bulk-change --revert=run-uuid

Marketplace screenshots for this workflow live in docs/assets/marketplace/ and are declared in capell.json so the Capell website can show the criteria, review, approval, content-first editor, layout areas, and public output states.

Public Rendering

Use Capell\LayoutBuilder\Actions\BuildPublicLayoutGraphAction when a public route, API, or package needs layout content without depending on the frontend renderer.

$graph = BuildPublicLayoutGraphAction::run(
    layout: $layout,
    page: $page,
    language: $language,
    containers: ['main'],
    includeHtml: false,
);

Payload contributors are tagged with Capell\LayoutBuilder\Contracts\PublicWidgetPayloadContributor::TAG. Contributors should return public-safe data or HTML only; do not expose admin state, editor-only metadata, private IDs, or unpublished content.

Widget variants and settings are stored as authoring state in widget meta, but public rendering receives only the sanitized presentation payload:

[
    'variant' => 'split-media',
    'spacing' => 'normal',
    'background' => 'default',
    'mediaPosition' => 'top',
    'cardsPerRow' => 3,
    'showCta' => true,
    'headingWidth' => 'normal',
    'anchorId' => null,
]

Public Blade must not query the database, lazy-load relationships, resolve widget contracts, expose raw meta, or include authoring selectors, signed URLs, diagnostics, package internals, schema, labels, or preview/admin view names.

Layout Areas

Layout areas let a theme expose named places where normal Layout Builder widgets can render outside the main page-body loop. The storage model stays unchanged: widgets still live inside layout containers, and containers may set meta.area.

  • Missing meta.area is treated as main for backwards compatibility.
  • main is built in and rendered by the normal main-content hook.
  • Themes and packages can register extra areas through Capell\LayoutBuilder\Support\LayoutAreas\LayoutAreaRegistry.
  • The Foundation Theme registers header, so editors can place normal widgets into the site header without hidden containers or a separate data model.

Register areas from a package service provider after the registry resolves:

use Capell\LayoutBuilder\Support\LayoutAreas\LayoutAreaRegistry;

$this->app->afterResolving(
    LayoutAreaRegistry::class,
    function (LayoutAreaRegistry $registry): void {
        $registry->register(
            key: 'header',
            label: __('capell-layout-builder::generic.header_area'),
        );
    },
);

If an area only applies to one active theme, pass the theme key:

$registry->register(
    key: 'announcement',
    label: __('capell-theme-client::layout_areas.announcement'),
    themeKey: 'client',
);

Public area rendering should use the package renderer rather than querying from Blade:

<x-capell::layout.area area="header" />

The area component reads the already-resolved layout containers and uses the stored CapellLayoutManager widget instances. Keep public Blade query-free and authoring-free; area keys are public placement data, but editor state, model IDs, field paths, signed URLs, and package/admin metadata must stay out of the HTML.

Apps and package seeders should use AttachWidgetToLayoutAreaAction when placing widgets into a named area. The action creates the area container when needed, normalizes the area key, preserves existing container metadata, and avoids duplicate widget/occurrence pairs.

use Capell\LayoutBuilder\Actions\AttachWidgetToLayoutAreaAction;

AttachWidgetToLayoutAreaAction::run(
    layout: $layout,
    area: 'header',
    widgetKey: 'announcement-bar',
    containerKey: 'site-announcement',
    containerMeta: ['container' => 'full'],
);

For full-bleed sections, separate the two width settings deliberately. The layout container owns the section band and background, so set its container meta to full. The widget or widget inside owns readable content, so keep its own container meta at the default contained width. This gives edge-to-edge backgrounds while text, media, and controls stay aligned with the site container.

Avoid solving this in widget Blade with w-screen, negative margins, or translate hacks. Those make the widget fight the layout system and usually break once the same widget is reused in another theme or container.

Reusable Presets

Saved agency presets are persisted in layout_presets and scoped to a required site_id with optional theme_key. Presets are layout-only by default: they deep-copy structure, selected widget variants, and settings without duplicating client content. Applying a preset revalidates site scope and regenerates duplicate anchors.

The older in-session LayoutPresetRepository remains only for temporary editor fragments; package and agency presets should use SaveLayoutPresetAction and ApplyLayoutPresetAction.

Visual Regression Manifests

Use capell:layout-builder-widget-visual-regression capture or assert to emit deterministic widget/variant/viewport fixture entries for a screenshot runner. The command supports --widget, --theme, --variant, --changed, --concurrency, and --ci-limit.

The command does not authenticate, generate signed routes, query tenant content, or use live media. Browser capture/compare remains the responsibility of the runner.

Docs

Editor Modes

content_first is the default mode. It shows editor-facing content groups from the current layout state and lets editors update assigned widget assets without navigating the full layout canvas.

layout_first opens the drag/drop layout builder directly. Keep it available for designers and implementers who need placement and structure control.

Both modes write through the same WidgetAsset persistence path.

Tests

Run the package suite from the packages monorepo:

vendor/bin/pest packages/layout-builder/tests --configuration=phpunit.xml

Run the focused public graph and package-boundary checks after changing public rendering or package ownership:

vendor/bin/pest packages/layout-builder/tests/Integration/PublicLayoutGraphActionTest.php packages/layout-builder/tests/Arch/LayoutBuilderPackageBoundaryTest.php --configuration=phpunit.xml
Overview A shorter package overview for marketplace and account review. Open

Layout Builder

Status: Available, schema-owning · Kind: package · Tier: free · Bundle: foundation · Contexts: admin, frontend, console · Product group: Capell Foundation

Layout Builder owns Capell's visual composition layer: reusable layouts, widgets, widget assets, content-first editing, public layout rendering, layout areas, presets, and widget visual-regression manifests.

It also gives admins a reviewed bulk change workflow for shared layout maintenance. Teams can define a typed operation, preview every affected layout and page, inspect before and after widget order, then approve the stored run only when the result is clear.

Install

composer require capell-app/layout-builder
php artisan capell:layout-builder-install

The package requires capell-app/admin, capell-app/block-library, capell-app/core, and capell-app/frontend through Composer. The isolated audit harness confirmed that block-library is installed as a hard Composer dependency.

Admin Surfaces

  • WidgetResource for reusable widgets, widget metadata, widget assets, and layout relationships.
  • LayoutResource, extending the core Layouts admin resource with package-specific table and editor behaviour.
  • Page schema extenders for layout/content-first editing and hero editing.
  • Layout schema extender for package layout fields.
  • A "Bulk change layouts" Filament action wizard with criteria, preview review, warnings, skipped layouts, and approval.
  • Livewire layout builder component and Filament assets.
  • Dashboard widgets for layout health and recent activity when enabled by the host admin surface.

Bulk Layout Change Review

Bulk changes are designed for layout maintenance that would otherwise become a fragile manual pass across many pages:

  • move breadcrumbs below a hero widget;
  • remove a deprecated widget from all active layouts;
  • swap the order of two widgets;
  • move a widget from a sidebar container into the main container.

The workflow is intentionally preview-first. ResolveLayoutBulkChangeTargetsAction finds candidate layouts. PreviewLayoutBulkChangeAction stores a run and per-layout result rows with original and proposed containers, hashes, page counts, structured diffs, warnings, and skipped reasons. ApplyLayoutBulkChangeRunAction only writes approved results whose current container hash still matches the preview.

That hash guard matters for real editorial teams. If someone edits a layout after the preview is generated, the apply step reports that result as drifted and leaves it alone instead of overwriting newer work.

Page-scoped widget_assets are migrated when a moved widget changes container or occurrence. Default widget assets are deliberately conservative in v1: previews warn or block when an occurrence change would make a default assignment ambiguous.

The command surface uses the same Actions as the Filament wizard:

php artisan capell:layouts:bulk-change --spec=/path/change.json --preview
php artisan capell:layouts:bulk-change --approve=run-uuid --json
php artisan capell:layouts:bulk-change --revert=run-uuid

Use the marketplace screenshot set in docs/assets/marketplace/ when explaining this feature publicly: the criteria wizard, review table, approved run state, content-first editor, layout areas, and public render output are all declared in capell.json.

Frontend Surfaces

  • Public layout components under resources/views/components/layout.
  • Main-content and named layout-area rendering through the resolved layout graph.
  • Public widget payload resolution through BuildPublicLayoutGraphAction.
  • Lazy public widget fragments through GET /_fragments/{reference} when a widget's presentation delivery mode is set to lazy_fragment.
  • Interaction triggers on widget type defaults and layout widget instances. Interactions can open registered widget targets through the frontend lazy widget endpoint or open encrypted Layout Builder widget fragments through the fragment endpoint.

Public Blade must stay query-free and authoring-free. Rendered HTML should not expose editor state, signed URLs, field paths, admin labels, internal model identifiers, or package diagnostics.

Lazy fragments are encrypted-reference-only. The reference is decoded and revalidated against site, page, layout, language, container, widget key, and occurrence before rendering. Invalid, replayed, or unsafe references return a generic 404. The fragment route is reserved so it cannot fall through to the CMS page resolver, and fragment responses use their own cache headers instead of the page HTML cache.

Presentation controls are available on widget type defaults and per-layout widget instances. Basic controls cover preset, width, and alignment. Advanced delivery controls, including lazy fragments, loading strategy, device visibility, connection requirements, and custom width, require the presentation.manage_advanced permission.

Interaction controls are available beside presentation controls. Layout Builder previews show interaction chips such as Play video -> widget so editors can see that a widget has a public interactive target without exposing the encrypted public reference or internal widget metadata.

Presentation And Delivery

Layout Builder uses the same presentation model as frontend widgets:

Surface Storage
Widget type default type meta.presentation
Layout widget instance layout widget meta.presentation

Settings resolve in the same order as widgets: instance override, type default, preset default, system default. The system default is server-rendered output, so saved layouts keep their existing behaviour unless an editor or widget type explicitly changes delivery.

Set delivery mode to lazy_fragment when the widget should render as a public placeholder and fetch its widget HTML through /_fragments/{reference}. Keep above-the-fold widgets server-rendered unless there is a clear performance or UX reason to defer them.

Interactions

Layout Builder supports interactions at two levels:

Surface Storage
Widget type default type meta.interactions
Layout widget instance layout widget meta.interactions

Instance interactions replace type defaults when present. The shared admin schema lets editors choose a target type and behaviour, then configure the target widget or fragment settings in the same flow.

Fragment interactions are a first-class Layout Builder use case. When an interaction target is fragment and no fragment_reference is stored, public rendering generates an encrypted reference to the current widget. That lets a widget render a small trigger upfront and fetch its heavier widget HTML later through /_fragments/{reference}.

Use fragment interactions for optional or expensive widget content, video/detail panels, comparison sections, below-the-fold content, or page experiences where click/visibility should control when the widget renders.

Use widget interactions when the target is a reusable Capell widget, such as a video player, form, gallery, or calculator. The target widget renders through the frontend lazy widget endpoint, not through a Layout Builder fragment.

Public previews and page output must not expose widget keys, layout IDs, page IDs, model IDs, component names, package namespaces, field paths, or editor metadata. Preview chips are admin-only and show human labels such as Play video -> widget; public triggers use generic runtime data and encrypted target URLs.

Visual Preview

The visual editor preview uses breakpoint-aware canvas width variables for desktop, tablet, and mobile frames. The preview tree compresses at narrower admin widths, and save controls stay in a sticky preview action bar so editors can save without losing their position in the canvas.

Screenshot Plan

Current committed screenshots need recapture before marketplace promotion: the existing files render a broken default shell/oversized black arc instead of usable Capell UI. Keep them as runner evidence only until the capture app loads the correct package assets and seeded states.

  • Widgets admin index.
  • Create/edit widget form, including widget assets.
  • Widget layouts relation manager.
  • Layouts admin index with Layout Builder table extensions.
  • Page form layout/content-first editor tab.
  • Hero editor page extension.
  • Public main content render.
  • Public named layout area render.
  • Layout health dashboard widget.
  • Recent activity dashboard widget.

Verification

  • Package tests: vendor/bin/pest packages/layout-builder/tests --configuration=phpunit.xml.
  • Harness install: composer require capell-app/layout-builder:4.x-dev -W, then php artisan package:discover --ansi and php artisan migrate --graceful --ansi.

Known Risks

  • Public render performance still needs an explicit query/time budget regression test for larger widget graphs.
  • Content-first and layout-first editor screenshots should continue to be captured separately because they exercise different editor states.
  • Layout health and recent activity dashboard widget screenshots should be added when the host admin dashboard enables those widgets.
History

Downloads and releases

Total downloads
0
Last downloaded
No downloads yet

Version history

1 release
  1. v1.0.0
    May 3, 2026
Write a review

Write your comment here. If you are not logged in, you will be asked to log in or create an account before it can be submitted.

Login or create an account is required before the review is saved.

No approved reviews yet. Reviews from eligible customers appear here after marketplace review.

Payment and licences

How marketplace purchases work

Capell keeps payment, account ownership, package receipts, and install access separate so a failed step can be recovered without guessing where the licence lives.

Who takes the payment?

Paid marketplace checkouts are sent to Stripe. Capell does not collect card details inside your CMS; Stripe confirms the payment and Capell activates the matching marketplace licence for the Capell account that approved the install.

How is the licence attached?

The licence is created against the signed-in Capell account first. When the hosted install flow returns to your CMS, Capell binds that licence to the requesting site and sends back the install authorization needed to continue.

Where does Anystack fit?

Anystack can be the package commerce and distribution surface for marketplace products. Capell still records the account licence and installed receipt, then relays later Anystack product or licence events only to connected sites that already have the package installed.

What happens if checkout is cancelled or expires?

No licence is created until Stripe confirms payment. If checkout is cancelled, start it again from the same review screen. If the quote expires, return to the marketplace in your CMS and start a fresh install flow so pricing and package metadata are recalculated.

What should I do if something looks wrong?

Keep the support reference shown on the review screen or receipt, then contact support before retrying with a different account. For missing licences, failed returns, or Anystack receipt mismatches, Capell can reconcile the Stripe session, Capell account, package receipt, and connected site from that reference.