Creating A Blog With Eleventy
Mục Lục
Creating A Blog With Eleventy
This article covers creating a blog from scratch using the Node.js powered static site generator
Eleventy
(aka 11ty).
Eleventy is a relatively new project that came out in 2018 and is quickly gaining traction. It’s up to an impressive 5,000 stars on GitHub already.
Eleventy keeps things simple and as you’ll see, really enables you to quickly create a fully functional site.
The blog we’ll create will contain a homepage with excerpts from our posts that can be clicked on to read the full post. Once we have our blog up and running, adding new posts will be as simple as adding new Markdown files. Eleventy will take care of generating HTML pages seamlessly for us as well as updating the homepage.
The code for this article is available on GitHub:
https://github.com/JonUK/eleventy-blog
A demo of the blog is hosted on Netlify:
https://dazzling-almeida-ca0492.netlify.com/
Creating the project
Create the folder eleventy-blog
for our project and then within this folder, create a new package.json
.
mkdir eleventy-blog
cd eleventy-blog
npm init
When the npm init
command is run, you will be asked a series of questions which can all be answered with the default values.
With the package.json
file in place, we can now install Eleventy locally as a development dependency.
npm install @11ty/eleventy --save-dev
Note that the package is within the scope @11ty
. Another completely different npm package exists with the name eleventy
.
Now let’s create the Markdown file About.md
for the about page on our blog.
#
About This Blog
This is my special place on the big World Wide Web
And with these steps alone, we are ready to have Eleventy generate our first HTML page. Run npx eleventy
.
npx eleventy
Writing _site/About/index.html from ./About.md.
Processed 1 file in 0.26 seconds
You should now have a new folder _site
created for you by Eleventy which contains the folder About
and inside this folder, the HTML file index.html
.
<
h1>
About This Blog</
h1>
<
p>
This is my special place on big World Wide Web</
p>
When Eleventy finds a template content file like a Markdown file, it generates an index.html
under a folder named the same as the original filename but without the file extension. E.g. the file Pages\Help.md
will result in index.html
being created under the folder _site\Pages\Help
.
The default output folder is _site
and the default input folder is the root directory. These can be configured via an .eleventy.js
config file in the root directory. Later in this article, we’ll create this file but for other reasons.
You can read more about configuration in the official documentation.
Serving up the site
Having Eleventy generate the HTML for our blog posts is great but while we’re developing and adding content, we really want to run a web server so we can see our pages in the browser and even better than that, have the browser update whenever we make changes. Well guess what… Eleventy has got this covered!
First we’ll add some script entries to our package.json
for the different Eleventy commands we’re going to use.
...
"scripts": {
"build": "npx eleventy",
"serve": "npx eleventy --serve",
"debug": "DEBUG=* npx eleventy"
},
...
Next we’ll create a basic layout file that will contain HTML markup to wrap around the HTML that Eleventy generates for each Markdown file. The layout file will ensure that the final output is valid HTML with <html>
and <body>
tags. The layout file will also contain standard elements we’d like on every page such as a header and footer.
Create the folder _includes
and within this folder create the file base-layout.njk
with the following content.
<!
DOCTYPE
html
>
<
html lang
=
"
en"
>
<
head>
<
meta charset
=
"
utf-8"
>
<
meta name
=
"
viewport"
content
=
"
width=device-width, initial-scale=1"
/>
<
title>
Eleventy Blog</
title>
</
head>
<
body>
<
header>
Site Header
<
a href
=
"
/About"
>
About</
a>
</
header>
<
main>
{{ content | safe }}
</
main>
<
footer>
Site Footer</
footer>
</
body>
</
html>
Layouts are searched for in the “includes folder”, which is _includes
by default. This can be configured to another location if required.
A .njk
file is a Nunjucks template that can contain, among other things, loops, conditional logic, special formatting and variable handling. With the line {{ content | safe }}
, the template will take the HTML generated from the Markdown file and combine this into the HTML generated for the layout template. The | safe
part runs a filter that ensures the HTML from the Markdown is not escaped.
You can learn more about Nunjucks on the official site
To use this layout, we need to add some front matter metadata at the top of our About.md
file.
---
layout: base-layout.njk
---
#
About this blog
This is my special place on the big World Wide Web
We can now start the local web server with hot-reloading via the following command.
npm run serve
Going to
http://localhost:8080/About
in a browser (your port may vary) shows our first page in all it’s glory!
If you change the Markdown file or the layout template, Eleventy will generate the new HTML and update the browser almost immediately. Similarly, if you create a new Markdown file, Eleventy will detect this and automatically generate a new HTML file.
Creating the homepage
What we’d like on our homepage is to list the recent blog posts we’ve added to our site. Let’s first create some dummy blog post Markdown files so we have some content to show. In a new folder posts\2019
create the files blog-post-1.md
and blog-post-2.md
with following content.
blog-post-1.md
---
layout: base-layout.njk
title: This Is My First Ever Post
date: 2019-05-30
tags: ['post']
---
This is the first post on my blog.
Eleventy is super fresh init.
blog-post-2.md
---
layout: base-layout.njk
title: How To Get Rich Quick
date: 2019-06-01
tags: ['post']
---
Buy the latest and greatest cryptocurrency that no-one has heard of.
##
Legal Stuff
We do not accept any liability for any loss or damage.
Note that each blog post is using the base-layout.njk
Nunjucks template and the title for each blog article is in the front matter metadata.
We’ll now create an index.njk
Nunjucks template for the homepage of our site. Create index.njk
in the root directory with the following content.
---
layout: base-layout.njk
pagination:
data: collections.post
size: 10
reverse: true
alias: posts
---
{% for post in posts %}
<
article>
<
h1>
<
a href
=
"
{{ post.url | url }}"
>
{{ post.data.title }}</
a>
</
h1>
{{ post.date }}
</
article>
{% endfor %}
Each of our blog articles has the tag of post
assigned to it in the front matter metadata. Eleventy provides a collection for each tag that’s been assigned, so we can access all our blog posts via the collection collections.post
. The pagination
metadata specifies that the variable posts
will be available in the template and will contain the last 10 blog posts. In the template, we’re then iterating through each post and outputting a link to the post and outputting the date.
Looking at the homepage, the date isn’t formatted in a particularly nice way and should be formatted to be easier to read. Eleventy has no in-built mechanism for formatting dates, so we’ll create our own “filter” to handle this. We’re going to use the npm package “moment” to handle date formatting so let get that installed.
npm install moment --save-dev
We’ll then create the Eleventy configuration file .eleventy.js
where we’ll add some date filters.
const
moment =
require
(
'moment'
)
;
moment.
locale
(
'en'
)
;
module.
exports
=
function
(
eleventyConfig
)
{
eleventyConfig.
addFilter
(
'dateIso'
,
date
=>
{
return
moment
(
date)
.
toISOString
(
)
;
}
)
;
eleventyConfig.
addFilter
(
'dateReadable'
,
date
=>
{
return
moment
(
date)
.
utc
(
)
.
format
(
'LL'
)
;
}
)
;
}
;
This will make the filters dateIso
and dateReadable
available for us to use within our Nunjucks templates. We’ll use both of these date formats as we’re going to output a <time>
element which uses the ISO format to assist with search engines understanding the date.
Update index.njk
to use the new filters.
...
<
time datetime
=
"
{{ post.date | dateIso }}"
>
{{ post.date | dateReadable }}</
time>
...
This results in our dates being much more readable.
Displaying excerpts on the homepage
When people view the homepage of our blog, we’d like to entice them to read a post by displaying the first part of it for them to see.
As of Eleventy v0.9.0
, the gray-matter
npm package that Eleventy uses to parse front matter can be configured to use an excerpt separator which makes an excerpt available in your template as page.excerpt
.
https://www.11ty.dev/docs/data-frontmatter-customize/#example-parse-excerpts-from-content
For the purposes of this article, we’re not going to use that approach and we’re going to take full control of excerpt handling ourselves via a “shortcode”. Add the following function at the very bottom of .eleventy.js
(after the module.exports
function).
function
extractExcerpt
(
article
)
{
if
(
!
article.
hasOwnProperty
(
'templateContent'
)
)
{
console.
warn
(
'Failed to extract excerpt: Document has no property "templateContent".'
)
;
return
null
;
}
let
excerpt =
null
;
const
content =
article.
templateContent;
const
separatorsList =
[
{
start:
'<!-- Excerpt Start -->'
,
end:
'<!-- Excerpt End -->'
}
,
{
start:
'<p>'
,
end:
'</p>'
}
]
;
separatorsList.
some
(
separators
=>
{
const
startPosition =
content.
indexOf
(
separators.
start)
;
const
endPosition =
content.
indexOf
(
separators.
end)
;
if
(
startPosition !==
-
1
&&
endPosition !==
-
1
)
{
excerpt =
content.
substring
(
startPosition +
separators.
start.
length,
endPosition)
.
trim
(
)
;
return
true
;
}
}
)
;
return
excerpt;
}
Then from within the module.exports
function, register the excerpt
“shortcode” with Eleventy.
eleventyConfig.
addShortcode
(
'excerpt'
,
article
=>
extractExcerpt
(
article)
)
;
This “shortcode” will take the HTML output from each article and return just the content between the HTML comments <!-- Excerpt Start -->
and <!-- Excerpt End -->
. If these comments aren’t found, it will instead return the content in the first paragraph.
Update the blog post Markdown files to wrap the <!-- Excerpt Start -->
and <!-- Excerpt End -->
HTML comments around the content you want to show as the excerpt for each post on the homepage. For example:
This is included on the homepage.
This will not be on the homepage.
Now we just need to use the excerpt
“shortcode” in the index.njk
homepage template. We’ll also add a “Read more” link at the bottom of each excerpt.
...
<
time datetime
=
"
{{ post.date | dateIso }}"
>
{{ post.date | dateReadable }}</
time>
{% excerpt post %}
<
a href
=
"
{{ post.url | url }}"
aria-label
=
"
Read more on {{ post.data.title }}"
>
Read more</
a>
...
The blog is starting to take shape nicely.
Blog post nested layout template
If we look at each of our blog posts, they don’t display the title or the date available in their front matter metadata. We can easily solve this by creating a nested layout template for use with blog post Markdown files. Create a new layout template post-layout.njk
in the folder _includes
with the following content.
---
layout: base-layout.njk
---
<
article>
<
h1>
{{ title }}</
h1>
<
time datetime
=
"
{{ date | dateIso }}"
>
{{ date | dateReadable }}</
time>
{{ content | safe }}
</
article>
This will output the post title and date for each post. This layout template itself uses base-layout.njk
layout, so it’s a nested layout template. To use this layout template, update the front matter metadata in each blog post Markdown file to layout: post-layout.njk
.
Congratulations, you’ve got yourself a blog setup where adding new posts and having the homepage update is as simple as creating a new Markdown file.
Show me the code!
The code for this article is available on GitHub:
https://github.com/JonUK/eleventy-blog
The latest version of the code in master
has a few additional changes to use Google Fonts and use a CSS file to smarten the site up.
A demo of the blog is hosted on Netlify:
https://dazzling-almeida-ca0492.netlify.com/