All the ways you can build a web based on content (IV)
Published: 2021-01-30 21:27:46

Photo by [Joaquim Campa](https://twitter.com/JoaquimCampa/status/1297069718554255361)

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](https://www.11ty.dev/). Eleventy can digest all kinds of template engines, but for our example we will be using [Nunjucks by Mozilla](https://mozilla.github.io/nunjucks/).

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.

```json
[
  {
    "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:

```html
<!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:

```html
---
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:
```html
---
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:

{% glitch all-the-ways--ssg app %}

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:
```json
"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.