Skip to main content

Documentation

No results found.
Features

Per-Record Detail Layouts

WebProCMS lets each individual blog post, event, or location choose which full-page detail design renders for it. One blog post can use a clean centered article layout, the next can use a two-column layout with a live sidebar of categories...

WebProCMS lets each individual blog post, event, or location choose which full-page detail design renders for it. One blog post can use a clean centered article layout, the next can use a two-column layout with a live sidebar of categories and recent posts, and a third can use an image-in-sidebar layout — all from the same site, picked per record on its edit screen. The choice is a single dropdown ("Detail Layout") with an optional side-by-side preview of each design rendered with that record's own content.


Why it exists

Most CMSs give you one detail template per content type — every blog post looks the same. WebProCMS ships several professionally-designed detail layouts per type and lets the editor pick the best one for each piece of content:

  • A photo-heavy travel post wants an image-sidebar layout that showcases the featured image.
  • A long-form tutorial wants a with-sidebar layout so readers can jump to related categories and recent posts.
  • A short announcement wants a clean, distraction-free article layout.

There's no developer involved and no theme switch — it's a per-record setting the content editor controls.

Where it's available

Content type Detail layouts available Picker shown?
Blog posts, Services, Portfolio, and every other content type Default (the type's generated layout) + the shared field-agnostic variants (Article, With Sidebar, Image Sidebar, and more) Yes, once ≥2 variants exist
Events Default (the type's generated layout) + a bespoke Event Detail design + the shared field-agnostic variants Yes
Locations Default (a bespoke Location Detail design) + the shared field-agnostic variants Yes

The picker only appears when a type has two or more designs to choose from, so it stays out of the way until it's useful. As more designs ship for a type, the picker lights up automatically.

Blog, events, locations, and every custom content type share one pool of field-agnostic content-detail variants rather than bespoke per-type designs: each variant is chrome (container width, etc.) wrapping the <x-content-type-fields> partial, which renders whatever fields the type declares — so a single set of variants serves every content type regardless of field shape. The type's own generated detail template is always present as the Default baseline, and a type can also ship its own bespoke, @requiresContentType-gated designs (like Events' Event Detail or Locations' Location Detail) alongside the shared pool. When an item needs more than a prebuilt variant can give, promote it to a custom item page instead.

How an editor uses it

  1. Open a blog post / event for editing.
  2. In the sidebar, find the Detail Layout card (under Tags for blog, under Featured Media for events).
  3. Pick a design from the dropdown — "Default (current page design)" keeps the site's standard layout; any other option switches just this record.
  4. Click Preview layouts to open a modal that shows each design rendered with this record's actual content. Use the ‹ › arrows to flip between designs, then Use this layout to select the one you're viewing.
  5. Save. The chosen layout takes effect on the public page immediately.

Setting the dropdown back to Default returns the record to the type's standard layout. The ? next to the Detail Layout label explains the field and how the dropdown's options are managed.

Adding your own design to the dropdown. The options are full-page "detail" designs in the Design Library. To add one without writing code, build a detail layout in the page editor and use Save to library with the Full-page design option ticked (under the matching detail sub-category) — it appears in this dropdown automatically. Custom designs are install-specific and survive CMS updates.

Changing the type-wide default

"Default" isn't fixed — an admin chooses which design is the default for all records of a type:

  • Blog: Dashboard → Blog → Settings → Templates tab → Default detail layout.
  • Events: Dashboard → Events → Settings → Detail page layout.

Pick any design and save. From then on, every post/event that hasn't set its own layout renders that design, and the per-record picker's "Default (current page design)" option follows the new choice (the previously-default design becomes a normal selectable alternate). The control only appears for types with two or more designs, so locations stays hidden until a second location design ships.

Under the hood the default is stored as a setting (detail_layout.default.{type}) and mirrored onto the show page: the chosen design's row is materialized (if not already present) and flagged as the default; all other layout rows become per-record-only. The render guard reads the baked flag, so switching the default adds zero queries to the public page.

How it works

Behind the scenes there are four pieces, all built into the CMS:

  1. The choice lives on the record. Each post / event / location has a detail_layout column holding the chosen design's name. Empty = "use the page default." New records start empty, so nothing changes until an editor opts in.

  2. The picker + preview modal on the edit form. The dropdown lists the page default plus every alternate design for that type. The preview modal renders each design full-page in a zoomed-out iframe, bound to the record being edited — so a preview of "Image Sidebar" for this post shows this post's title, image, and copy, not a generic sample.

  3. Lazy materialization on save. The first time a record picks a non-default layout, the CMS writes that layout's rows onto the type's detail page as ordinary, fully-editable editor rows — each wrapped in a lightweight visibility guard. The site's original default layout is wrapped in a matching "default" guard at the same time. This happens automatically on save; the editor never has to touch page structure.

  4. A query-free render guard. On the public page, each layout's rows are wrapped in an @if guard that checks the current record's detail_layout. Only the matching layout renders — the others short-circuit before any of their content or queries run. The decision reads a value already loaded with the record, so it adds zero database queries on both a cache hit and a cache miss.

Self-healing

The layout rows are re-materialized on every save of a record that has a layout chosen — not just when the choice changes. So if someone deletes a layout's rows in the page editor, the next save of any record using that layout puts them back. There's no separate "repair" step.

In the page editor

Because materialized layouts are ordinary editor rows, they coexist cleanly with everything else:

  • Each layout's rows appear in the page editor's sidebar, independently editable — restyle the "Image Sidebar" layout without affecting "With Sidebar."
  • A small chip on each row card shows "Default layout" or "Layout: <Name>" so it's obvious why two layouts are stacked on one page.
  • The "Preview as " dropdown works as expected — pick a record and the editor preview shows only that record's chosen layout.
  • Swapping a layout row to a different design from the row library keeps its per-record guard intact (the guard is the contract for which records see the slot; the design behind it is yours to change).

Editing a layout's content is per-layout: changing copy in the "Image Sidebar" layout doesn't sync to "With Sidebar," because the two have different field sets. That's intentional — they're different designs.

Performance & caching

  • Zero added queries on render. The active-layout decision is an in-memory check on the already-bound record, wrapped in a Blade @if (not a stored visibility rule), so the inactive layouts never execute their bodies — no slot capture, no related-posts or recent-posts queries.
  • Cache-friendly. Each record's page is its own full-page cache entry, and the layout decision is baked into that cached HTML. The page cache is never bypassed.

Limits (current release)

  • A record pointing at a layout whose rows were deleted in the editor renders only its un-gated rows until its next save (then self-heals). There's no proactive background sweep.
  • Per-layout content is independent by design — there's no cross-layout content sync.