Templates and External Markdown Files

Markdown files in Eleventy are like a README.md file you find on GitHub. They have the same syntax. They can also have front matter added. They are used to contain data and content, which is usually then inserted into a template file.

Layouts

Layouts are a type of template that has content meant to wrap other content, e.g. HTML that wraps markdown. The markdown file indicates what layout it is to use. Eleventy will then assemble them and create a folder for the new file. The folder will be named after the markdown file and will contain an index.html file.

Create a new file, about.md. This will be the markdown file. Create a new folder _includes. Inside of that create a file layout.html. This will be the layout template. Eleventy defaults to _includes to find templates files.

Here is the about.md file :

---
layout: layout.html
---

# Animals About Page

This is the Animals about page
				

Here is the layout.html file :

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    {{ content }}
</body>
</html>
				

In the about.md file is the front matter specifying the layout template, in this case _includes/layout.html. Since Eleventy defaults to the _includes folder, that part doesn't need to be added. Below that is the markdown content.

In the layout.html file can be seen a tag {{ content }}. Eleventy will automatically format and insert the content of the about.md here.

A new folder _site/about should appear, and inside that an index.html file (the About page). In that file should be the content :

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <h1>Animals About Page</h1>
    <p>This is the Animals about page<</p>
</body>
</html>
				

The markdown text with the # has been automatically inserted into a new <H1> tag, with the remaining content inserted into a new <P> tag.

Folder Structure

Again, Eleventy is for creating static web sites. It assumes there is no database backend or server rendering, that the pages are delivered as they are stored on the web host. It also assumes that the web hosting is intended for static web sites, and does not offer things like redirects or other server processing.

To achieve the preferred URL style of domain/folder/ as opposed to page names like index.html, it creates a folder for every page that would be delivered, which then contains an index.html. Since index.html is always a default for web servers, it is not needed in the URL, providing a clean address structure. The index.html also helps prevent exposing any other files that might be in that folder, thus adding a little more security.

Layouts and Markdown With Front Matter

Both the layout and the markdown can contain and use front matter as well as markdown content.

Add a variable in the about.md file :

---
layout: layout.html
title: Animals About Page 2
---

# {{ title }}

This is the Animals about page version 2
				

You should see the <H1> text of the About page change to match the title variable.

Add a variable in the layout.html file :

---
disclaimer: This is a disclaimer from layout.html
---
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    {{ content }}
    <p>{{ disclaimer }}</p>
</body>
</html>
				

The front matter disclaimer variable should appear in the About page as well as the markdown and front matter from the markdown. You should also see that the title variable carried over from the markdown.

Now try adding a title variable to the layout front matter.

---
disclaimer: This is a disclaimer from layout.html
title: Title from layout.html
---
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    {{ content }}
    <p>{{ disclaimer }}</p>
</body>
</html>
				

The title on the page shouldn't change. Eleventy has a cascade that prioritizes which variables are finally applied. In this case, the markdown front matter wins out. In the layout file, try changing it to this :

---
disclaimer: This is a disclaimer from layout.html
title2: Title from layout.html
---
<!DOCTYPE html>
<html>
<head>
    <title>{{ title2 }}</title>
</head>
<body>
    {{ content }}
    <p>{{ disclaimer }}</p>
</body>
</html>
				

Now the layout front matter title shows up instead. More information about Eleventy's data cascade can be found here.

Collections

A collection groups data based on a common tags value. If the front matter in a markdown file contains a particular tags value, it will be included in a collection and provided to templates that call for it. The tags variable can contain multiple values, which means the same front matter can belong to multiple collections.

We'll be creating a set of blog post markdown files which use _includes/layout.html, so you'll need to revert it so that you're using the markdown file's title:

---
disclaimer: This is a disclaimer from layout.html
title2: Title from layout.html
---
<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    {{ content }}
    <p>{{ disclaimer }}</p>
</body>
</html>
				

Create a markdown file post-01.md.

---
tags: post
title: Blog Post 01
date: 2019-01-01
layout: layout.html
---

# First Blog Post
Welcome to the blog.
				

Create a postlist.html file in the project root:

<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
    <ul>
        {% for post in collections.post -%}
        <li>{{ post.data.title }}, {{ post.data.date }}</li>
        {% endfor -%}
    </ul>
</body>
</html>
				

A new folder _site/postlist should appear, containing an index.html file. The file should contain :

<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
        <li>Blog Post 01, Mon Dec 31 2018 19:00:00 GMT-0500 (Eastern Standard Time)</li>
</body>
</html>
				
There should also be a new folder _site/post-01, containing an index.html file. This is the actual permalink page for the post. The file should contain:
<!DOCTYPE htm>
<html>
<head<
    <title>Blog Post 01</title>
</head>
<body>
    <h1>First Blog Post</h1>
    <p>Welcome to the blog.</p>

    <p>This is a disclaimer from layout.html</p>
</bod>
</htm>
				
This is the post markdown using the original layout template created before.

Create two more markdown files, post-02.md and post-03.md.

---
tags: post
title: Blog Post 02
date: 2019-01-02
layout: layout.html
---

# Second Blog Post
Down to business.
				
---
tags: post
title: Blog Post 03
date: 2019-01-03
layout: layout.html
---

# Third Blog Post
Even more news today.
				
The _site/postlist/index.html file should now look like:
<!DOCTYPE html>
<html>
<head>
    <title>Blog Post</title>
</head>
<body>
    <ul>
        <li>>Blog Post 01, Mon Dec 31 2018 19:00:00 GMT-0500 (Eastern Standard Time)</li>
        <li>>Blog Post 02, Tue Jan 01 2019 19:00:00 GMT-0500 (Eastern Standard Time)</li>
        <li>>Blog Post 03, Wed Jan 02 2019 19:00:00 GMT-0500 (Eastern Standard Time)</li>
    </ul>
</body>
</html>
				
And you should have two more folders, _site/post-02 and _site/post-03 with their index.html files.

It can be seen that Eleventy gathered up all the markdown files with the post tag, and included them in the list items in postlist/index.html.

To allow better organizing, the three post-*.md files can be moved from the root into a dedicated folder, e.g. posts. Eleventy will still process them the same way, except the post-* folders will be under _site/posts now.

Linking to a Collection Item

To turn the list items in postlist/index.html into links, edit postlist.html to add {{ post.url }} as a link :

<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
    <ul>
        {% for post in collections.post -%}
        <li><a href="{{ post.url }}">{{ post.data.title }}, {{ post.data.date }}</a></li>
        {% endfor -%}
    </ul>
</body>
</html>
				
The resulting list items should all now look similar to this :
<li><a href="/posts/post-01/">Blog Post 01, Mon Dec 31 2018 19:00:00 GMT-0500 (Eastern Standard Time)</a></li>
				

Collections and Markdown Content

When a markdown file uses a layout template, that template only uses the content found in that specific markdown, via the {{ content }} value. As noted previously, that content is preformatted as HTML before insertion into the layout.

However, when using the collection object, the markdown content must be acquired from the particular collection being iterated. In the above example, the value would be found in post.templateContent. Note that it is not part of post.data.

The post.templateContent is not raw data, but does contain the preformatted HTML. But as part of a collection, Eleventy considers it all a value, and will encode it as character entities before inserting it into the template, so that the HTML code displays on the page. To keep the HTML intact, post.templateContent can be piped to the safe filter.

To see this work, add post.templateContent and safe to postlist.html :

<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
    <ul>
        {% for post in collections.post -%}
        <li><a href="{{ post.url }}">{{ post.data.title }}, {{ post.data.date }}</a>
        {{ post.templateContent | safe }}
        </li>
        {% endfor -%}
    </ul>
</body>
</html>
				

This should result in the formatted content for each post appearing below the links in the list.

Eleventy and Date Values

It can be seen that Eleventy is not handling date as a string but as a date value, and assumes date is entered as a UTC time, and then adjusts it for the local time zone.

Another thing about date is that it is a variable generated by Eleventy anyway, based on the file creation date. This means that even if there was no date in the front matter, it would still be available to templates. Adding your own date value in front matter overrides the automatic value.

If the front matter has a value with the form of YYYY-MM-DD, Eleventy will process it as a date, regardless of the variable name. And if that format is seen in the filename, Eleventy will let that override the file creation date.

The date value displayed might not be in a desired format. Refer to the section on using a filter to format a date.