Template Tags Reference
Audience: Site Builder.
PageCraft exposes two tag families:
- Field tag —
{your_field_name}and its modifiers. Use inside{exp:channel:entries}to output the field's stored data. - Module tags —
{exp:page_craft:…}for sitewide concerns (resolved HTML, locked global header / footer).
Field tag
The field tag works like any other custom field tag. Its type= parameter selects which slice of the stored JSON you want.
{exp:channel:entries channel="pages" url_title="{segment_3}"}
<style>{my_field type="css"}</style>
{my_field type="html"}
<style>{my_field type="sectioncss"}</style>
{/exp:channel:entries}
Parameters:
type="html"— the canvas HTML, with dynamic content already resolved (template embeds rendered, structure-driven nav menus injected). Default.type="css"— the page-level main stylesheettype="sectioncss"— the accumulated per-section CSSruntime="yes"— also enqueue the front-end runtime JS/CSS (for sections that include ContentBox plugins like sliders, accordions, FAQ blocks). Defaults tonoso simple text-only pages don't pull the runtime.
Module tags
{exp:page_craft:render}
The all-in-one front-end tag. Returns a complete render of a specific entry's PageCraft field: an inline <style> block (main + section CSS), the resolved HTML wrapped in <div class="is-wrapper">, and — unless disabled — the enqueued runtime JS/CSS.
{exp:channel:entries channel="pages" url_title="{segment_2}" limit="1"}
{exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
{/exp:channel:entries}
Parameters:
entry_id="{entry_id}"— required. The entry to render. Inside{exp:channel:entries}use the{entry_id}variable; the tag also accepts a literal id.field="page_builder"— required. The PageCraft field's short name. This must match the field exactly — a wrong name produces<!-- PageCraft: no data found for entry N, field X -->and renders nothing.runtime="yes"(default) /no— enqueue the ContentBox runtime so plugin sections (sliders, accordions, nav menus, FAQ) initialise on the front end.wrapper="yes"(default) /no— wrap the output in<div class="is-wrapper">. Leave this on. The runtime'sprepareContent()selects.is-wrapperunconditionally; if no.is-wrapperexists on a page that boots the runtime, init throwsCannot read properties of nulland all PageCraft content on the page fails to render.class="..."— extra classes added to the wrapper div.
Diagnostics: render never fails silently. If output is missing, View Source for an HTML comment:
| Comment in source | Cause |
|---|---|
entry_id and field parameters are required |
entry_id resolved to 0 (parse order) or field is blank |
no data found for entry N, field X |
Entry matched but the field short name is wrong, or the entry has no saved data |
| (no comment at all) | {exp:channel:entries} matched zero entries — render never ran |
rendervshtml: userenderwhen you want the full package (styles + wrapper + runtime) for one entry. Use{exp:page_craft:html}(below) when you want just the resolved HTML and you're managing CSS/runtime yourself.
{exp:page_craft:html}
Returns the entry's PageCraft content as fully resolved HTML — no wrapper, no CSS, no runtime. Use it when you are handling those yourself.
Parameters — both required, and the tag renders nothing without them:
entry_id="{entry_id}"field="page_builder"— the field's short name
{exp:page_craft:html entry_id="{entry_id}" field="page_builder"}
This tag does not resolve "the current entry" on its own. Earlier versions of this page said it did and that you needn't know the field name; neither has ever been true. A bare
{exp:page_craft:html}returns nothing at all.Inside
{exp:channel:entries}, reach for the field tag instead —{page_builder}or{page_builder type="html"}. The channel module resolves a field per row, so it cannot hit the parse-order problem described underrender, where{entry_id}has not been substituted by the time a nested module tag runs. The module tags are for when you already have an id — a layout variable, a known entry, a second channel.
{exp:page_craft:css}
Outputs a <style> block containing the entry's main CSS and section CSS combined. Use it when you are rendering with html and managing assets yourself.
Parameters — both required, and the tag outputs nothing at all without them:
entry_id="{entry_id}"— the entry to readfield="page_builder"— the PageCraft field's short name
{exp:channel:entries channel="pages"}
{exp:page_craft:css entry_id="{entry_id}" field="page_builder"}
{exp:page_craft:html}
{/exp:channel:entries}
Earlier versions of this page listed
{exp:page_craft:main_css}and{exp:page_craft:section_css}. Neither has ever existed — the module has no such methods, so both tags rendered nothing.cssis the tag that does the job, and it returns the two stylesheets together. To output them separately, use the field tag'stype="maincss"andtype="sectioncss".
{exp:page_craft:runtime_assets}
Enqueues the ContentBox runtime JS and CSS and outputs nothing. For templates that want explicit control over when assets load rather than letting render enqueue them.
{exp:page_craft:global_header}
Renders the locked global header for the current site (see Global Header & Footer).
Parameters:
wrapper="yes"(default) /no— wrap in a<header>elementclass="..."— extra classes on the wrapperruntime="yes"— enqueue the front-end runtime alongside
Returns an empty string if no header has been locked.
{exp:page_craft:global_footer}
Same as global_header but for the footer slot. Same parameters.
Canvas Assets
Custom CSS and JS that should ride alongside the editor canvas (and optionally the front-end render). Set per field or globally.
- Custom CSS — additional stylesheet that loads inside the canvas iframe. Useful when your section markup uses utility classes (Tailwind, custom design tokens) the canvas doesn't otherwise have access to.
- Custom JS — additional script that loads inside the canvas iframe. Common case: third-party plugin code you want active so editors see exactly how the front-end will look.
Both are injected into the editor frame. The runtime="yes" parameter on the field tag (or the {exp:page_craft:html} tag) controls whether the same assets enqueue on the front-end.
Security: the addon strips outer <script> / <style> tags and HTML comment wrappers before persisting, so editors can paste asset blocks copied from another tool without the wrapping tags accidentally breaking ContentBox's iframe init. See Actions/CustomCss.php and Actions/CustomJs.php if you want to audit the strip rules.
Asset placeholders
Four placeholders are replaced by the extension's template_post_parse hook, so they work in layouts and embeds as well as templates. They emit the CSS and JS that block and plugin sections need:
| Placeholder | Where it goes | What it emits |
|---|---|---|
{pagecraft_styles} |
<head> |
<link> tags for block/plugin CSS |
{pagecraft_css} |
<head> |
inline <style> from <!-- pc:css --> directives |
{pagecraft_scripts} |
before </body> |
<script> tags for block/plugin JS |
{pagecraft_js} |
before </body> |
inline <script> from <!-- pc:js --> directives |
Without them, plugin sections — sliders, nav menus, accordions, FAQs — render as inert markup.
Put {pagecraft_styles} before your own stylesheets
<head>
{pagecraft_styles}
<link rel="stylesheet" href="/assets/css/your-site.css">
</head>
ContentBox ships utility classes with common names — .opacity-8, spacing and sizing helpers — and many carry !important. If your theme defines the same names, whichever sheet loads later wins, so loading PageCraft's first lets your own CSS take precedence.
Watch .opacity-* in particular: ContentBox reads it as a percentage (opacity-8 = 8%) while many themes read it as tenths (opacity-8 = 80%). A theme class that survives elsewhere can be silently rescaled on a PageCraft page. Where it collides, use a value only one of the two defines — opacity-80 rather than opacity-8.
When to use field tag vs. module tag
- Field tag (
{my_field}) — preferred when you're already inside{exp:channel:entries}and you know the field name. Slightly more efficient because it avoids the extra channel lookup the module tag does. - Module tag (
{exp:page_craft:html}) — preferred for shared layouts that render different entries via different channels. Works without knowing the field name as long as the channel only has one PageCraft field.
Reference
- Field tag:
ft.page_craft.php::replace_tag() - Module tags:
mod.page_craft.php—render(),html(),css(),runtime_assets(),global_header(),global_footer() - Canvas Assets:
Actions/CustomCss.php,Actions/CustomJs.php