Layouts & partials
Layouts and partials are the other two page primitives — the same page store, a different type. Together with pages they make composition a first-class idea: everything on a page is a labeled partial, dropped into place.
Layouts — the chrome around a page
A layout (type = layout) is the shell that wraps a page: the header, footer, nav, and outer markup a page renders inside. A page names its layout with the layout_key column, pointing at a layout row's stable page_key.
When Tiger_Cms_Renderer::render() renders a page that has a layout_key, it:
- Renders the page body by its format → HTML.
- Fetches the layout row and renders its body, passing the page's HTML in as the
contentview var. - The layout emits that HTML wherever it places the
shortcode.
So a layout is just another content row whose body places where the page should go:
<main class="container py-5">
</main>
Layouts aren't publish-gated — they're infrastructure, fetched by key regardless of status (Tiger_Model_Page::fetchByKey), and they cascade per org like everything else (a tenant's own layout wins over the global one).
A layout row is an author-editable template. It's distinct from a theme's layout files, which are presentation chrome resolved by path (see Theming). Reach for a CMS
layoutrow when you want the template itself to be editable content; theme files own the site's structural chrome.
Partials — reusable fragments
A partial (type = partial) is a named fragment you compose into pages and layouts: a hero, a call-to-action, a pricing band, a footer. You place it by reference with the shortcode:
name is the partial's page_key. At render time the shortcode looks up the published partial row (org cascade — an org's own partial wins over the global one) and renders its body in place. Because it's placed by reference, editing the partial once updates it everywhere it's dropped.
Partials render recursively: a partial can itself contain , , and nested s. A cycle-and-depth guard (max depth 10, no repeated name on the stack) stops infinite loops — a skipped partial leaves an HTML comment rather than breaking the page.
"Everything is a labeled partial"
The payoff of these three primitives plus is that a whole site is composed from labeled fragments. A visually-built layout is itself just partials around the content slot:
Change the header partial once and every page using that layout updates. Build a page from a stack of section partials and reorder them without touching the sections themselves. This is the composition model the visual builder leans on.
The widget in the builder
In the GrapesJS visual builder, partials show up as draggable blocks. Dropping a partial widget renders a live preview of the fragment in the canvas, but exports the shortcode, not the frozen markup. So the design stays dynamic and auth-filtered at view time: the builder saves the reference, and the partial is resolved fresh on every render. (Editing a fragment in the builder is the page builder inverted — the layout becomes locked context and the fragment is the one editable region.)
Contrast this with a block (type = block), the builder's copy-in library fragment: dropping a block inlines its HTML into the page, detached from the source. A partial is a live reference; a block is a one-time copy. Blocks are covered in The visual builder.
See also
- Pages — the store, formats, slug dispatch, cascade
- Shortcodes —
,,, and registering your own - The visual builder — the partial widget and blocks
- Theming — theme layout files vs CMS layout rows