Layout templates
A layout template is common in most Apostrophe apps. As the name suggests, it contains the markup that surrounds page content and is mostly consistent across the website. Website navigation and footers are both usually in the layout template, whether directly as markup or included from template partials.
Let's look at a simple layout template file at views/layout.html.
{% extends data.outerLayout %}{# 👈 Extending outerLayout.html from core #}
{# 👇 Inserting markup into a lower level template block #}
{% block beforeMain %}
<div>{# Open page wrapper #}
<header>
<img src="/images/logo.png" alt="Organization logo">
<nav>{# Website navigation #}</nav>
{% if not data.user %}<a href="/login">Login</a>{% endif %}
</header>
<main>{# Open main tag #}
{% endblock %}
{% block afterMain %}
</main>{# Close main tag #}
<footer class="bp-footer">
<p>
© Apostrophe Technology, Inc.
</p>
</footer>
</div>{# Close page wrapper #}
{% endblock %}You might notice is that this does not have essential web page elements such as a head or body tag. That is because the first thing this template does is extend another template:
{% extends data.outerLayout %}data.outerLayout is a reference to a lower level layout template from Apostrophe core that includes those critical HTML elements, markup required by Apostrophe, and the template block structure that project-level templates use. The lowest-level templates in any project should extend this. (See that file on Github if you're interested.)
This layout template then includes two template blocks, beforeMain and afterMain, containing markup that wraps most page content.
{% block beforeMain %}
{# Page opening markup... #}
{% endblock %}
{% block afterMain %}
{# Page ending markup... #}
{% endblock %}These two are before and after the main block in the base layout template linked above. By using them in views/layout.html, they override the matching blocks in the extended template. They are great places to put the site navigation, site footer, and other markup that should always wrap the main content of the page.
The most important templates blocks from that core layout template are:
| Template block name | What is it? |
|---|---|
startHead | A block at the beginning of the head tag for inserting metadata tags. |
title | The contents of the title tag. This defaults to using the title of the page or piece (for show pages). |
extraHead | A block at the end of the head tag for inserting metadata tags. |
bodyClass | A block in the body tag's class attribute for adding a class for when that template is used. |
beforeMain | A block before the main content block. Usually used for the website header. |
main | The primary block for page content. Most page template markup goes inside main. |
afterMain | A block after the main content block. Usually used for the website footer. |
extraBody | A block at the end of the body tag. |
The layout template and any page, index page, or show page template could use these blocks to overwrite them or add to them (using the super() tag).
NOTES
The beforeMain, main, and afterMain blocks are inside the section that Apostrophe refreshes regularly during content editing. Any script tags inside those blocks will run an indeterminate number of times during editing. Be especially careful when using event handlers. As a reminder, any widget-related JavaScript belongs in a widget player.
layout.html is a naming convention in Apostrophe, but is not a required file name. You can name it anything you like. Just remember to extend data.outerLayout and update page templates to extend it by its new name.
RTL language support: The outerLayout template automatically applies the correct text direction (dir attribute) to the <html> element based on your locale configuration. See the localization guide for more information.
Writing a layout in JSX
A layout can be written as views/layout.jsx instead. The block-based shape above doesn't translate directly — a JSX layout doesn't override outerLayoutBase's blocks piecemeal, it renders the whole invariant part itself and exposes props for what each page needs to supply. This also resolves a case the Nunjucks version above can't express cleanly: beforeMain and afterMain open and close a single <div> across two separate blocks, which has no equivalent when there's no block system — a JSX layout renders that wrapping <div> in one place, as one component.
function Header({ user }) {
return (
<header>
<img src="/images/logo.png" alt="Organization logo" />
<nav></nav>
{!user && <a href="/login">Login</a>}
</header>
);
}
function Footer() {
return (
<footer className="bp-footer">
<p>© Apostrophe Technology, Inc.</p>
</footer>
);
}
export default function(data, { Extend }) {
return (
<Extend
templateName={data.outerLayout}
title={data.title}
main={
<div>
<Header user={data.user} />
<main>{data.main}</main>
<Footer />
</div>
}
/>
);
}A page template then extends it and passes only its own content as main — it never needs to know what the header or footer render, which is what the Nunjucks version's super() calls would otherwise be for:
<Extend templateName="layout" main={<PageContent page={page} />} />TIP
data is deliberately not destructured in the layout above. A page template's own props — title, main — arrive as this template's data, so data.main here is the page's content prop, not something from the page document itself.
See Coming from blocks and super() for the general pattern this follows, including the transitional shape for a project converting one page at a time while layout.html is still Nunjucks.
WARNING
Converting a project's layout to JSX only works one direction: a .jsx layout can be extended by both .jsx and .html pages, but a .html template can never extend a .jsx layout. Any core-provided Nunjucks template that extends the project's layout by name — @apostrophecms/page's notFound.html is the one every project has — needs a project-level .jsx shadow with the same name, or it will throw instead of rendering once the layout itself is JSX.