Pages and page types
Every page in an Apostrophe website is assigned a "page type". The page type tells Apostrophe what template to use to render the page and what configuration to apply. Configurations will often at least include the field schema for the page type.
Creating a page type
Apostrophe core only includes a "Home page" type with some basic default content options. You will likely need your own page types, which you create by adding modules that extend @apostrophecms/page-type
and instantiating them in app.js
.
If we add a string field for the subtitle and an area field for rich text and images, the Default page type would look like:
See the field schema page for more on configuring fields.
TIP
We can add functionality to the default home page type by adding a configuration file for it at modules/@apostrophecms/home-page/index.js
. Add new fields to it as in the example above and this core page type will be ready for additional content possibilities. Or it will be once we update its page template... See below.
Page template essentials
Each page type requires a template. The only exception to that rule is if a page type extends another page type that already has a template.
Page templates are added in a views
directory for the page type as page.html
. The template for the previous example's default page would be modules/default-page/views/page.html
. A very simple page template for the Default page might look like this:
{# modules/default-page/views/page.html #}
{% extends "layout.html" %}
{% block main %}
<header>
<h1>{{ data.page.title }}</h1>
{% if data.page.subtitle %}
<p>{{ data.page.subtitle }}</p>
{% endif %}
</header>
{% area data.page, 'main' %}
{% endblock %}
There are a number of things at work here.
The template is extending a layout.html
template
{% extends "layout.html" %}
layout.html
is a base level template used in official Apostrophe essentials starter kit and placed in views/layout.html
. It is used to add markup for things that belong on every page, such as the website navigation and footer. It extends the outerLayout.html
template from Apostrophe core, but provides a layer to customize the page wrapper while not overwriting outerLayout.html
.
The layout template might look something like this:
{% extends data.outerLayout %}
{% block beforeMain %}
<div>
<header>
{# Page header code: logo, navigation, etc. #}
</header>
<main>
{% endblock %}
{% block main %}
{# Page body content. Pages templates normally override this. #}
{% endblock %}
{% block afterMain %}
</main>
<footer>
{# Page header code: contact information, secondary navigation, etc. #}
</footer>
</div>
{% endblock %}
We are inserting page template markup in a template block
{% block main %}
{% endblock %}
Apostrophe uses the Nunjucks template language, which has a block system for injecting markup into lower-level templates. The block system involves placing a block
tag in the root-level template file, then using those blocks in higher-level templates to insert markup. Since we used the main
block here it will overwrite the layout template's main
block.
Page data is on data.page
{{ data.page.title }}
Templates have access to a data
object containing information about the Apostrophe application and current context data. In page templates, data.page
contains data for the active page. For our Default page, that includes the title, subtitle, "main" area, and lots of other information.
Naming specific properties in the double brackets syntax, , prints them in the template.
{% if data.page.subtitle %}
<p>{{ data.page.subtitle }}</p>
{% endif %}
Nunjucks offers additional tags, including the {% if %}
conditional tag, to help work with data in templates.
TIP
If you want to know what is available in a template object, you can log it in your terminal using the template method apos.log()
. This looks like:
{{ apos.log(data.page) }}
The widget area is added using the area
tag
{% area data.page, 'main' %}
This is a special tag in Apostrophe used to let editors add and manage content widgets to the page. After the area
tag name, we pass the tag the field's context, which is our page, followed by the field name. We configured it in the index.js
file to use two widget types. While editing the page, the user will have access to a menu to add widgets of those types.
We'll explore areas more in the areas guide.
TIP
To overwrite the home page type template, create a template file for it at modules/@apostrophecms/home-page/views/page.html
and add template markup.
Activating page types
There is one more step to make a page type available to use: You'll need to add it to the core page module's type
option. This configures the "Type" field for pages.
This is a core module option, but you can add your own configuration by giving it an index.js
file in your project: modules/@apostrophecms/page/index.js
. You'll then configure it's types
option with all page types you want to allow.
Each type needs a name
matching the module's name and a label for editors. See the reference section for other core page module options.
Connecting pages with page tree navigation
There are many ways to build navigation with Apostrophe. One is to base site navigation on the page tree. The "page tree" refers to the parent-child relationship between pages. For example, the home page is the parent of all top-level pages, which may have subpages of their own.
Pages can be organized into a page tree hierarchy while adding them or through the page manager interface.
Apostrophe templates have data available to add navigation based on the page tree. This includes:
Data object | What is it? |
---|---|
data.home | Home page data. It is similar to the data on data.page , but always references the home page. |
data.home._children | Page data for pages one level below the home page in the page tree. |
data.page._ancestors | Page data for the ancestors of the active page, starting with the home page. |
data.page._children | Page data for pages one level below the active page. |
By default, one level of children are available on each ancestor, as well as on the home page and data.page
.
With that available data, we could construct navigation for the website header using the Nunjucks {% for %}
loop tag. The layout.html
beforeMain
block could look like:
{# views/layout.html #}
{% block beforeMain %}
<div>
<header>
{# 👇 Adding our navigation wrapper. #}
<nav>
<ul>
{# 👇 Referencing `data.home._children` and looping over them. #}
{% for page in data.home._children %}
<li>
<a href="{{ page._url }}">{{ page.title }}</a>
</li>
{% endfor %}
</ul>
</nav>
</header>
<main>
{% endblock %}
This is looping over the home page's child pages, printing their URLs and titles into links. This is simply one way to add navigation using the page data in templates.