ContentTypes / Introduction
Note: You are currently reading the documentation for Bolt 4.0. Looking for the documentation for Bolt 5.2 instead?
All content in Bolt is stored in the database in a logical and flexible fashion. In general, when you're building a website, you have an idea what kind of content you're going to be managing with the website. Most websites have some sort of 'pages' for generic stuff like 'about us' or 'Company History'.
Most websites will also have some form of news-like items, that are shown based on the date that they were published. Some other sites might have 'book reviews' or 'event dates' or even completely different content. All of these different types of content are called ContentTypes in Bolt, and you can add as many different ContentTypes as you need.
Each ContentType is made up of Fields, you can read more about Fields and what fields are available in the Fields Definition section.
All content on your website is part of one specific ContentType, which automatically defines which fields that piece of content has, which in turn specifies how that piece of content is structured. Each one of those pieces of content is called a Record, and is stored in the database. For example, a single 'event' is a Record of ContentType 'events' and a single 'page' is a Record of ContentType 'pages'.
When you're creating a page on a website that shows listings of several Records, you're using an Array of Records. For instance, if you create a page that has 'the five latest events', you'll be using an Array of 5 'event' Records of ContentType 'events'.
Before we dive into the details, we'll give you a quick example of a simple ContentType, how it's stored, and how you can access it in templates to display on your site.
An Example: News items¶
In this example, we'll create a very simple ContentType for news items. Each
news item will have a title, a photo, and some text. We'll also be using some
of the fixed Fields, like the slug
, the ownerid
and the various dates.
To add this ContentType, edit the file config/bolt/contenttypes.yaml
, and add
the following to the bottom or top of the file:
news:
name: News
singular_name: News Item
fields:
title:
type: text
class: large
slug:
type: slug
uses: title
photo:
type: image
text:
type: html
height: 300px
record_template: newsitem.twig
Tip: This file is in the YAML format, which means that the indentation is important. Make sure you leave leading spaces intact.
This creates a new ContentType 'news'. Its name is 'News', and a single record is named 'News Item'. We've defined fields for 'title', 'slug', 'photo' and 'text'. The 'record_template' defines the default template to use, when displaying a single record in the browser.
Note: You should always ensure that the key
that defines each of the ContentTypes and the value of the name
are the same. In the above example, we start with the key news:
,
and the name is set to name: News
. If these do not match, Bolt
will show an error message.
Save the file and refresh the Dashboard screen in your browser. If you do this, you'll see your new ContentType in the sidebar, ready for use. Sweet!
Note: In the following examples we're going to tell you to make modifications to the default `base-2021` theme. This is actually a very bad practice, because future updates of Bolt will override any changes made to that theme. If you're going to make your own theme, make a copy of the `base-2021` theme, and do your modifications in the copy.
To add a listing of these news items to the website, edit the twig template
theme/base-2021/index.twig
. Most likely, it'll contain an include for a
header and some other things. Add the following to the HTML-code, preferably
somewhere below the header section:
{% setcontent newsitems = "news" latest limit 4 %}
{% for newsitem in newsitems %}
<article>
<h2><a href="{{ newsitem|link }}">{{ newsitem.title }}</a></h2>
{{ newsitem|excerpt }}
<p class="meta"><a href="{{ newsitem|link }}">Link</a> -
Posted by {{ newsitem.author.displayname }}
on {{ newsitem.createdAt|date("M d, ’y")}}</p>
</article>
{% endfor %}
Most of the above example will seem pretty straightforward, but all of the specific template tags are explained in detail in the chapter about Content in templates.
When you refresh the front page of the website, you should see four news items
listed on the page. You can click the title to go to the news item on a separate
page, but it will use the default record.twig
template. In the ContentType we
defined the template as newsitem.twig
, but it doesn't exist. Create the file
in the theme/base-2021/
folder, and add the following HTML-code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>{{ newsitem.title }}</title>
</head>
<body>
<article>
<h1><a href="{{ newsitem|link }}">{{ newsitem.title }}</a></h1>
{% if newsitem.photo is not empty %}
<div class='imageholder'><img src="{{ newsitem.image|thumbnail(480, 480) }}"></div>
{% endif %}
{{ newsitem.text }}
<p class="meta"><a href="{{ newsitem|link }}">Link</a> -
Posted by {{ newsitem.author.displayname }}
on {{ newsitem.createdAt|date("M d, ’y")}}</p>
</article>
</body>
</html>
Tip: If you're curious about the different
{{ tags }}
in this bit of code, read the Template documentation.
In the frontend of the website, in your templates, all content is accessible as
an array. If you're accessing one record, it will be an array containing the
fields, taxonomies and metadata. If you're accessing a set of records, it will
be an array of arrays. I.e. {{ page.title }}
for the title of a page or
{{ events.3.date }}
for the date of the fourth event in an array.
Tip: Although it's possible to access an array
of records by its index number, this is not used very often in practice. It's
much more common to use a loop like {% for event in events %}
, to
iterate over all of the events
, and then use them seperately as a
single event
.
If you're building a template and are unsure of what a certain variable contains
or how the fields are named, use {{ dump(foo) }}
, where 'foo' is the name of
your record or array.
This is explained in detail in the section The structure of a Record.
Defining ContentTypes¶
ContentTypes in Bolt are defined in the file config/bolt/contenttypes.yaml
.
You can edit this file directly, or from within the Bolt interface under
Configuration > ContentTypes. Each distinct group of content can have its own
ContentType, to enable the user to store the content as needed. Fields can be
added later on, and settings can be changed, so nothing is set in stone.
The general structure of each ContentType is:
name:
option: value
option: value
option: value
..
The name
defines the name of the ContentType, and it should be a 'safe'
version of the name:
option below. Basically this means that it should be a
lowercase version, without any special characters. Spaces should be replaced
by hyphens. Like this:
pages:
name: Pages
singular_name: Page
..
cafes:
name: Cafés
singular_name: Café
..
blog-posts:
name: "Blog Posts"
singular_name: "Blog Post"
..
The available options are:
Option | Description |
---|---|
name |
The name of the ContentType, as it should be shown on screen or in the browser. It should be plural, if possible. |
slug (optional) |
This determines the slug of the ContentType, and therefore the URLs that are generated for this ContentType. When omitted, the slug will be automatically generated from the name . |
singular_name |
The name of one Record in the ContentType. This should be singular. For example, if the ContentType's name is 'Pages', this should be 'Page' |
singular_slug (optional) |
This determines the slug of a single record in this ContentType, and therefore the URLs that are generated for these records. When omitted, the slug will be automatically generated from the singular_name . |
fields |
The fields that make up the content in this ContentType. See the Fields Definition section for details. |
taxonomy (optional) |
An array listing the different taxonomies used by this ContentType. For example [ categories, tags ] . See the page on Taxonomies for details. |
relations (optional) |
An array listing the different relations available to this ContentType. See the page on Relations for details. |
record_template |
The default template to use, when displaying a single Record of this ContentType. The template itself should be located in your theme/foo/ folder, in Bolt's root folder. This can be overridden on a per-record basis, if one of the fields is defined as type templateselect . |
record_route |
The name of the route that should be matched for the record page of this ContentType. For more on routing, read the routing documentation. |
listing_template |
The default template to use, when displaying an overview of Records of this ContentType. The template itself should be located in your theme/foo/ folder, in Bolt's root folder. |
listing_records (optional) |
The amount of records to show on a single overview page in the frontend. If there are more records, the results will be paginated |
listing_sort (optional) |
The field used to sort the results on. You can reverse the order by adding a '-'. For example title or -datepublish . |
sort (optional) |
The default sorting of this ContentType, in the overview in Bolt's backend interface. For example -datecreated . |
records_per_page (optional) |
The amount of records shown on each page in the Bolt backend. If there are more records, they will be paginated. |
show_on_dashboard (optional) |
When set to false the ContentType will not appear in the 'Recently edited …' list on the dashboard page. |
show_in_menu (optional) |
When set to false the ContentType will show in a submenu instead of as a top level menu. Can also be set to a word or sentence to group ContentTypes under different menus. |
default_status (optional) |
Use this to set the default status for new records in this ContentType, i.e. published , held , draft or timed . |
searchable (optional) |
A boolean value to determine whether this ContentType should show up in search results. |
viewless (optional) |
When set to true , routes will not be set for the ContentType listing, or the records themselves. Useful for creating Resource ContentTypes. |
singleton (optional) |
When set to true , the Bolt UI will adapt to give a fluent experience for ContentTypes with one post. Like a complex homepage or general settings. Singletons. |
title_format (optional) |
Is used to determine the format of the title. For example if you have two fields for firstname and lastname you might put {firstname} {lastname} here. |
excerpt_format (optional) |
Is used to determine the format of the excerpt. For example if you have two fields for highlights and content you might put {highlights}: {content} here. |
icon_many (optional) |
A Font Awesome icon to be used in the sidebar for this ContentType. For example: fa:cubes . See the full list of available icons in the FA gallery. |
icon_one (optional) |
A Font Awesome icon to be used in the sidebar for a single record of this ContentType. For example: fa:cube . |
locales (optional) |
The locales the content can be entered in. i.e. ['en', 'nl', 'pt_BR', 'es'] . |
Automatic titles versus title_format
¶
As you might have noticed in the screenshots above or in the Bolt backend
itself, Records in listing overviews and in the "Recently edited" panel are
always shown with a linked title. Even if the ContentType does not actually
have a field named title
, Bolt will make a reasonable assumption as to what
the "Title" should be. By using title_format
you can override this, if Bolt
doesn't provide correct titles for a ContentType.
title_format and excerpt_format¶
The title_format
and excerpt_format
options both support all fields defined
in the content type, the author
, status
and id
of the record. For example:
pages:
name: Pages
singular_name: Page
title_format: "{id} ({status}): {title}" # e.g. "35 (published): About us
excerpt_format: "{author}: {content}" # e.g. John Doe: Whole content until character limit...
fields:
title:
type: text
content:
type: html
Singleton ContentTypes¶
You can set the option to create a Singleton ContentType, which basically is a ContentType that only contains exactly one record. For example, you can create a Singleton where you can store the site's social media links and basic contact info, that you can then use globally in your templates. Another common usecase is for a logical place to put the "Homepage" content.
More details and examples can be found in Making a Singleton ContentType.
Grouping ContentTypes in the sidebar¶
If you have a larger number of ContentTypes, the sidebar in Bolt's backend might
look too cluttered. To make your the sidebar tidier, you can group several
ContentTypes together in the sidebar menu, using the show_in_menu
option.
For example:
entries:
name: Entries
…
show_in_menu: false
showcases:
name: Showcases
…
show_in_menu: false
The result of this example, is shown in the following screenshot:
Tip: If you provide a name, like show_in_menu:
Foo
, then that name will be used, instead of the generic "Other content"
label.
The structure of a Record¶
Every record is an object, that contains the information of that record, as well as some meta-information and its taxonomy.
At the topmost level, it contains the following items:
Item | Description |
---|---|
id |
The unique identifying number of this record in the database. |
contentType |
A string containing the key of the content type. |
author |
An array, containing information about the user, like the displayname, email address, etcetera. |
status |
The current status of this record. Can be either published , depublished , held , timed or draft . |
createdAt |
The timestamp of when the record was first created. |
modifiedAt |
The timestamp of when the record was last edited of modified. |
publishedAt |
The timestamp when the record was published, or when it will be published. |
depublishedAt |
The timestamp when the record was depublished, or when it will be depublished. |
fields |
An array with metadata of the Fields on this record. To get field values, see below. |
contentTypeDefinition |
An array with metadata of the ContentType that this record belongs to. |
taxonomies |
An aray with metadata of the Taxonomies attached to this record. |
The object also contains a generic getter for a record field values. Fetch the value
using {{ record.field_name }}. If the field_name
is not found, it throws an exception
if it's invoked from code, and returns null if invoked from within a twig template.
(In templates we need to be more lenient, in order to do things like {% if record.foo %}..{% endif %}
.
Note: We can not rely on {% if record.foo is defined %}
, because it always returns true
for object properties.)
There is also a variety of built-in twig helper filters and functions to get various pieces of record data.
If you're building a template and are unsure of what a certain variable
contains or how the fields are named, use {{ dump(foo) }}
, where 'foo' is the
name of your record or array. In most templates, {{ dump(record) }}
will work
as a generic fallback for whatever the name of your record is.
Advanced: YAML Repeated Nodes¶
In order to make your ContentType definitions more compact, and consistent, you
can use YAML repeated nodes. Bolt has a special YAML key called __nodes
that
it will use only for repeated nodes, and not create a ContentType or table for.
These nodes then become selectable in a ContentType definition.
Each node is defined by an key_name: &node_name
with the fields then included,
and indented below.
## Defaults nodes. Does not create a ContentType
__nodes:
record_defaults: &record_defaults
title:
type: text
class: large
group: main
slug:
type: slug
uses: title
content_defaults: &content_defaults
image:
type: image
attrib: title
body:
type: html
height: 300px
group: content
template_defaults: &template_defaults
template:
type: templateselect
filter: '*.twig'
group: meta
In the above example, we have the record_defaults
node that defines title
and slug
fields, a content_defaults
node that defines the image
and body
fields, and a template_defaults
node that defines our template selector.
Using the above nodes we could simplify a default Pages
ContentType to look
like this:
pages:
name: Pages
singular_name: Page
fields:
<<: *record_defaults
teaser:
type: html
height: 150px
<<: *content_defaults
<<: *template_defaults
taxonomy: [ chapters ]
records_per_page: 100
Keeping fields together in a group¶
If you have defined groups in a YAML repeated node, you can add a field from a specific ContentType to one of these groups.
In the example below, the slider field will appear in the tab media.
Example:
__nodes:
record_defaults: &record_defaults
title:
type: text
class: large
group: main
slug:
type: slug
uses: title
content_defaults: &content_defaults
image:
type: image
group: media
(...)
pages:
name: Pages
singular_name: Page
fields:
<<: *record_defaults
slider:
type: imagelist
group: media
(...)
Couldn't find what you were looking for? We are happy to help you in the forum, on Slack or on Github.