Photo by Joaquim Campa
In Part III we were talking about static servers being cheap... or even free! Here's a curated list of providers you can use:
https://serverless.css-tricks.com/services/hosting
How to use hosting static servers like those and still being able to neatly separate the data from the website itself?
Solution 3: Static Site Generator
With an SSG, you will mix up data with templates to obtain a fully static site. This is exactly how some CMSs work indeed. Compared with SSR (discussed in Part II), the main advantage is that the processing of data is moved outside the web server. Let's see how our FruitLovers site would look now and we will discuss later about who should choose this approach.
For this stack we will be using a grrrrreat SSG called Eleventy. Eleventy can digest all kinds of template engines, but for our example we will be using Nunjucks by Mozilla.
Our data will be the same, as always. That's also the beauty of it. You can keep the data, change the way you process and create the website out of it, and the end product would look the same to a visitor.
[
{
"name": "lemon",
"title": "Lemons, our classic",
"price": "2€",
"image": "images/lemon.jpg"
},
{
"name": "strawberry",
"title": "Strawberries, less sugar than lemons!",
"price": "3€",
"image": "images/strawberry.jpg"
}
]
In Eleventy, if we save this in _data/fruits.json
it will be available directly as a collection we can iterate over on our templates.
The main layout template is as you would expect:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{title}}</title>
<style>
{% include 'styles.css' %}
</style>
</head>
<body>
<header>
<h1>🍋 FruitLovers</h1>
<p>
If life gives you lemons...
</p>
</header>
{{ content | safe }}
<footer>
FruitLovers © 2021
</footer>
</body>
</html>
The title will be replaced at {{ title }}
. And the content fed to this template will be replaced in {{ content | safe }}
.
The pipe |
is an operation to input in safe
the output of content
so that HTML characters are not escaped. We did something very similar when we were using the Handlebars template engine in part II. Then we were using 3 curly braces instead of 2, remember? It's the same idea 🙂
Read this for more info about double escaping in layouts: https://www.11ty.dev/docs/layouts/#prevent-double-escaping-in-layouts
If we save it in _includes/layouts/default.njk
then we can use it directly by name, like in the template for our home page:
---
layout: layouts/default
title: "FruitLovers - Home"
---
<main>
<h2>
Fruits you can buy from us
</h2>
<ul>
{% for fruit in fruits %}
<li>
<a href="/products/{{ fruit.name }}.html">
{{ fruit.title }}
</a>
</li>
{% endfor%}
</ul>
</main>
Here we are using a Nunjuck's tag called for
. fruits
represent the array of fruits from the _data/fruits.json
defined before. We can declare a name for the unit, fruit
so it's easier to read. This looks like a super neat way to create lists, doesn't it?
Our product page is a bit more tricky. Remember we have to remove the actual fruit from the list of fruits: we called this list restOfFruits
. Here, instead of creating a new list with the fruit removed, we're using a Nunjuck's tag called if
. And inside the loop of fruits, only proceed if our fruit and list item in the loop is different. We're using Eleventy's page.url
to know which fruit the actual page is for.
In this case, in order to create pages out of all the fruits, we will be using Eleventy's pagination feature. See more Eleventy's pagination here:
https://www.11ty.dev/docs/pagination/
Here's how our product page template would look using Eleventy's pagination:
---
layout: layouts/default
tag: product
pagination:
data: fruits
size: 1
alias: fruit
permalink: "products/{{ fruit.name }}.html"
eleventyComputed:
title: FruitLovers - {{ fruit.title }}
---
<main>
<a href="/index.html">← Home</a>
<h2>
{{ fruit.title }}
</h2>
<p>
Today's price is: {{ fuit.price }}
</p>
<img width="300" src="{{ fruit.image }}">
<h2>
Other fruits you can buy from us:
</h2>
<ul>
{% for fruit in fruits %}
{% if not page.url.includes(fruit.name) %}
<li>
<a href="/products/{{ fruit.name }}.html">
{{ fruit.title }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</main>
Now here's the end result in Glitch:
Using an SSG such as Eleventy is a 2-part process. First we create the site, and secondly we upload it to our static server. In my Glitch example, this is the start
script:
"start": "eleventy --serve --input=src --output=_site"
That means we're creating the site out of the data in src
and outputting it at _site
.
By the way, have you noticed whatever is being created in
_site
is exactly the same code we had in our Part I example made out only with HTML & CSS?
We're also serving it directly with --serve
, which is OK for when we're in development mode. The second part of the process involves copying the _site
folder to your static server, wherever that might be. That's where you should use the hosting static servers I linked at the beginning of this article!
SSG is a great solution when your data seldom changes and the number of pages is reduced. If you wanted to manage a site like Amazon with an SSG, you would have to process most of the pages continuously, which is far from optimal.
Most small sites like restaurant websites would be a great fit for SSG.
I hope you enjoyed this series of articles as much as I did writing them for you.
I'll answer any question promptly and feedback of any kind is welcome.
Thanks for reading.
Top comments (0)