Skip to content
pagecraft

Template Examples

Audience: Site Builder.

Copy-paste starting points for wiring PageCraft entries into front-end templates. They progress from the simplest possible render to a full multi-page site with global chrome.

Before you start — two common causes of missing output:

  1. Use your real field short name. Every field="…" below uses field="page_builder". Replace it with your actual PageCraft field short name (Develop → Channels → Fields). A wrong name renders nothing and emits <!-- PageCraft: no data found … -->.
  2. Keep the asset placeholders. {pagecraft_css} and {pagecraft_styles} belong in <head>; {pagecraft_js} and {pagecraft_scripts} belong before </body>. Without them, plugin sections won't load their CSS or JavaScript. Put {pagecraft_styles} before your stylesheets so your CSS wins collisions. See Template Tags Reference.

If a page comes up blank, View Source and check for an HTML comment — see the diagnostics table in Template Tags Reference.


1. Out of the box — the minimal render

The smallest template that renders a PageCraft entry. No routing, no chrome. Good for confirming the pipeline works before adding anything else.

global/_wrapper (layout)

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>My Site</title>
    {pagecraft_css}
    {pagecraft_styles}
</head>
<body>

    {layout:contents}

    {pagecraft_js}
    {pagecraft_scripts}

</body>
</html>

site/page (the template that renders an entry)

{layout="global/_wrapper"}

{exp:channel:entries channel="default_page" limit="1" status="open"}
    {exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
{/exp:channel:entries}

This grabs the most recent open entry and renders it. render supplies the inline CSS, the .is-wrapper div, and the runtime — so the layout only needs the four asset placeholders.


2. Out of the box — URL-routed pages

One template that serves any page by its url_title from the last URL segment. This is the typical "flat pages" setup.

site/page

{layout="global/_wrapper"}

{exp:channel:entries channel="default_page" url_title="{segment_1}" limit="1" require_entry="yes"}
    {exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
{/exp:channel:entries}
  • url_title="{segment_1}" — matches /about → entry with url_title about. Use {segment_2} if the template lives one group deep, or {last_segment} for nested/Structure URLs.
  • require_entry="yes" — a clean miss instead of a half-rendered page when the URL matches nothing.

Home page note: an empty URL means {segment_1} is blank, which matches no entry. Handle home with a dedicated condition — see example 4.


3. Mixed — out of the box render + custom EE markup around it

PageCraft renders the page body, but you hand-author the surrounding template: your own <h1>, breadcrumbs, sidebar, other channel fields, an entries loop of related posts, etc. Mix freely — render just drops resolved HTML into wherever you place it.

site/page

{layout="global/_wrapper"}

{exp:channel:entries channel="default_page" url_title="{segment_1}" limit="1" require_entry="yes"}

    <nav class="breadcrumbs">
        <a href="{path='home'}">Home</a> &rsaquo; <span>{title}</span>
    </nav>

    <article class="page-body">
        {!-- PageCraft-built body --}
        {exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
    </article>

    {!-- a normal custom field, rendered by hand --}
    {if custom_summary}<aside class="summary">{custom_summary}</aside>{/if}

    <section class="related">
        <h2>Related pages</h2>
        {related_entries:related_pages}
            <a href="{title_permalink='site/page'}">{title}</a>
        {/related_entries:related_pages}
    </section>

{/exp:channel:entries}

The key idea: render is just one tag among many inside the entries loop. Everything outside it is ordinary EE templating.

3b. Mixed — split CSS / HTML / runtime manually

When you need the page CSS in <head> (cleaner than render's inline <style>) but the HTML lower down, use the granular tags instead of render. Use the field tag (fastest, you're already in the loop) or the equivalent module tags.

{layout="global/_wrapper"}

{exp:channel:entries channel="default_page" url_title="{segment_1}" limit="1" require_entry="yes"}

    {!-- field-tag form: page_builder is YOUR field's short name --}
    {if no_results}{redirect="404"}{/if}

    {!-- main + section CSS pushed to the <head> via a layout variable --}
    {layout:set name="page_styles"}
        <style>{page_builder type="css"}</style>
        <style>{page_builder type="sectioncss"}</style>
    {/layout:set}

    <h1>{title}</h1>
    {page_builder type="html" runtime="yes"}

{/exp:channel:entries}

…and in the layout <head>, output {layout:contents}-set styles:

{if page_styles}{page_styles}{/if}
{pagecraft_css}
{pagecraft_styles}

type="html" defaults to runtime="no". Add runtime="yes" whenever the page uses plugin sections, or the sliders/menus won't initialise.


4. All custom — full site with global header & footer

A complete production layout: fixed global header, segment-based routing, a dedicated home case, and a global footer. This is the pattern to grow into once the basics render.

global/_wrapper (layout)

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>{if segment_1}{segment_1}{if:else}Home{/if} &middot; My Site</title>

    <style>
        .site-header{position:fixed;top:0;left:0;width:100%;z-index:9999}
        .site-header-spacer{height:70px}
    </style>

    {pagecraft_css}
    {pagecraft_styles}
</head>
<body>

    {!-- Global header on every page. Keep wrapper="yes" (the default) so the
         runtime always finds a .is-wrapper element and prepareContent() can't
         crash. --}
    <div class="site-header">{exp:page_craft:global_header}</div>
    <div class="site-header-spacer"></div>

    {layout:contents}

    {exp:page_craft:global_footer}

    {pagecraft_js}
    {pagecraft_scripts}

</body>
</html>

site/page (router with a home case)

{layout="global/_wrapper"}

{if segment_1 == "" OR segment_1 == "home"}

    {!-- Home: newest open entry --}
    {exp:channel:entries channel="default_page" url_title="home" limit="1" require_entry="yes"}
        {exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
    {/exp:channel:entries}

{if:else}

    {!-- Inner page: matched by URL --}
    {exp:channel:entries channel="default_page" url_title="{segment_1}" limit="1" require_entry="yes"}
        {exp:page_craft:render entry_id="{entry_id}" field="page_builder" runtime="yes"}
    {/exp:channel:entries}

{/if}

Why global_header keeps its wrapper

global_header / global_footer default to wrapper="yes", emitting a <div class="is-wrapper">. Do not set wrapper="no" on chrome that contains plugin sections (a nav menu, social links, etc.). The runtime's prepareContent() runs document.querySelector(".is-wrapper").classList.add(...) with no null guard — if the header boots the runtime but has no .is-wrapper, init throws and the entire page fails to render, main content included. The fixed-position styling lives on the outer .site-header div, so the inner .is-wrapper doesn't affect layout.


5. All custom — multiple PageCraft fields on one entry

If a channel has more than one PageCraft field (e.g. a page_header builder and a page_body builder), target each by name. render is per-field, so call it once per field.

{layout="global/_wrapper"}

{exp:channel:entries channel="landing" url_title="{segment_1}" limit="1" require_entry="yes"}

    <header class="lp-hero">
        {exp:page_craft:render entry_id="{entry_id}" field="page_header" runtime="yes"}
    </header>

    <main class="lp-body">
        {exp:page_craft:render entry_id="{entry_id}" field="page_body" runtime="yes"}
    </main>

{/exp:channel:entries}

The runtime and CSS enqueue is de-duplicated across calls, so multiple render tags on one page won't double-load assets.


Quick reference

You want… Use
Whole page, one tag, simplest {exp:page_craft:render entry_id="{entry_id}" field="…"}
HTML only, manage CSS yourself {your_field type="html"} or {exp:page_craft:html}
Page CSS in <head> {your_field type="css"} + {your_field type="sectioncss"}
Sitewide header / footer {exp:page_craft:global_header} / {exp:page_craft:global_footer}

See Template Tags Reference for every parameter.

For live HTML examples of section authoring, open the annotated developer reference.