pbBlocks
Renders the constructor blocks attached to a resource — the page body assembled in the builder.
[[!pbBlocks]]Parameters
| Parameter | Default | Description |
|---|---|---|
parent | current resource | Whose blocks to render — the same name the owner has in pbList, pbResources and pbMenu. |
limit | Render only the first N blocks. | |
offset | 0 | Skip the first N blocks. |
tpl | Chunk that receives each block as item. Without it every block renders through its own chunk, the one its constructor was configured with. | |
toPlaceholder | Put the output into a MODX placeholder and return nothing. | |
showLog | Print why nothing was rendered; manager only. | |
tplWrapper | pageblocks.blocks.wrapper | Chunk wrapped around the whole output. Applies with and without tpl. |
Plus tplEmpty and outputSeparator — those two only when tpl is given.
The wrapper is a default, not an option
tplWrapper is the .pb-page container, and that is the only place the component's design tokens are declared — --pb-content-max, --pb-section-gap and the rest of pb.blocks.css are set on it, not on :root. Lose the wrapper and every block loses the tokens it sizes itself by.
So it is applied on both render paths. To drop it — when your own site template already provides the page container — pass it explicitly empty: &tplWrapper=``. An empty value means "no wrapper", not "use the default": the snippet checks whether the parameter was passed at all, not whether it has a value.
An empty block list gets no wrapper either way, so you never get a stray empty <div>.
How output is chosen
Without tpl the snippet calls each block's own render(), which resolves the chunk from the block's chunk and chunk_path. That is the normal case: the builder already knows how each block looks.
With tpl you take that over — useful when the same blocks need different markup in a different place, or when you want to wrap each one.
Only published, only this language
The snippet uses the resource's blocks() relation, so it returns blocks that are published (published_at set and in the past), not soft-deleted, and belong to the current language. Unpublished blocks stay invisible on the front end without any extra condition on your side.
Examples
Body of the current page
[[!pbBlocks]]Blocks of another page
[[!pbBlocks? &parent=`15`]]First three blocks, then the rest
[[!pbBlocks? &limit=`3`]]
...
[[!pbBlocks? &offset=`3` &limit=`100`]]From Fenom
{foreach $modx->resource->blocks()->get() as $block}
{$block->render()}
{/foreach}Nothing rendered?
Check published_at on the blocks before looking at routes or templates — an unpublished block is silently skipped.