An intro to
the JAMStack & Eleventy

Luciano Mammino (@loige)

May 18, 19, 20 - 2021

Static Site generators... wait, what?!

content & templates

static HTML pages & assets

This is the core of a static site generator!

What are we talking about? I need examples!

A programming blog

A recipes website

A movie reviews website

A photo book

An online portfolio

A podcast website

...

And even single-page applications!

Let me introduce myself...

I'm Luciano (🇮🇹🍕🍝) 👋

👨‍💻  Senior Architect

Co-Author of Node.js Design Patterns  👉

Connect with me:
 

  loige.co (blog)

  @loige (twitter)

  lmammino (github)

We are business focused technologists that deliver.


Accelerated Serverless | AI as a Service | Platform Modernisation

Do you want to work with us?

Some static sites I built

METALSMITH

A Java API client!

Why do we care?

It's easy

It's generally cheap

It can scale easily

It can be more secure

We can build fast websites

We can still use dynamic data (API, CMS, etc.)

We can still add dynamic features

Which tool should I use?

+350 tools listed!

Why do I like Eleventy?

It's written in Node.js

It's so simple that you don't need a "starter"
(but there are many if you want)

Fast & lightweight

Not opinionated

Simple to build incrementally on top of it

Makes me feel productive...

OK. Now, Let's play with eleventy!

Install + Hello world

Custom config

Templates, frontmatter & Layouts

Live reload

Copying static files

Custom global data

Collection API

Using dynamic data

Pagination API

mkdir 11ty-sample-project
cd 11ty-sample-project
npm init -y

npm i --save-dev @11ty/eleventy

echo "# My sample Eleventy website" > index.md

node_modules/.bin/eleventy --watch --serve

Project scaffold

// .eleventy.js

module.exports = function (config) {
  return {
    dir: {
      input: './src',   // default: `.`
      output: './build' // default: _site
    }
  }
}

Custom config

Templates

  • markdown (.md), Nunjucks (.njk) and many other file types are called templates
  • These files can be used as a skeleton to generate pages
  • Eleventy will automatically search for them in our source folder and, by default, it will generate a page for each and every one of them

Frontmatter

  • frontmatter is an optional section at the top of a template that can be used to define additional contextual metadata.
---
name: someone
age: 17
---
Rest of the file

Frontmatter

  • Data in the frontmatter can be referenced in the templates
---
name: someone
age: 17
---
{{ name }} is {{ age }} years old!

Layouts

  • Layouts are used to wrap the generated pages in a common HTML structure
  • Especially useful for Markdown files
  • Layouts can also have a frontmatter
---
title: My default title
---
<!DOCTYPE html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>{{ title }}</title>
</head>
<body>
  <main>
    {{ content | safe }}
  </main>
</body>
</html>
src/_includes/base.njk
---
layout: base
---

# Hello from Eleventy

This is a simple Eleventy demo

To use a layout you can use the special frontmatter property "layout" in a template:

Eleventy uses BrowserSync to give you live-reload 🚀

Copying static assets

src/_includes/style.css
html, body {
  background-color: #eee;
  margin: 0;
}

main {
  box-sizing: border-box;
  max-width: 1024px;
  min-height: 100vh;
  padding: 2em;
  margin: 0 auto;
  background: white;
}

Copying static assets

// .eleventy.js

module.exports = function (config) {
  config.addPassthroughCopy({
    './src/_includes/style.css': 'style.css'
  })

  // ...
}
---
title: My default title
---
<!DOCTYPE html>
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>{{ title }}</title>
  <link rel="stylesheet" href="/style.css"/>
</head>
<body>
  <main>
    {{ content | safe }}
  </main>
</body>
</html>
src/_includes/base.njk

Global data files

// src/_data/site.js

module.exports = {
  author: 'Luciano Mammino',
  copyrightYear: (new Date()).getFullYear()
}
  • Define globally available data
  • JavaScript code that gets executed before templates are rendered
  • Can be static or dynamic (e.g. fetch data from an API or a DB)

Global data files

<main>
  {{ content | safe }}
  <hr/>
  <small>
    A website by {{ site.author }} 
    - &copy; {{ site.copyrightYear }}
  </small>
</main>
  • The filename defines how you access the data in templates

Collection API

---
tags: ["posts", "javascript"]
---

## An introduction to the spread syntax

**Spread syntax** (`...`) allows an iterable such as an array 
expression or string to be expanded in places where zero or 
more arguments (for function calls) or elements 
(for array literals) are expected, or an object expression 
to be expanded in places where zero or more 
key-value pairs (for object literals) are expected.
  • You can tag a template to be part of one or more collections
  • Use the special frontmatter entry "tags"

Collection API

---
title: "All posts"
layout: base
---

<h1>All posts</h1>

<ul>
  {% for post in collections.posts %}
    <li>
      <a href="{{ post.url }}">
        {{ post.data.title }}
      </a>
    </li>
  {% endfor %}
</ul>
  • Eleventy allows you to access all the metadata of templates for a given tag using the special collections.tagName global data

USING Dynamic DATA

npm i --save-dev @11ty/eleventy-cache-assets
  • What if we want to build a website using data from an API?
  • For instance this one: https://ghibliapi.herokuapp.com/films/
  • We don't want to fetch the data after every single change!

USING Dynamic DATA

// src/_data/movies.js

const Cache = require('@11ty/eleventy-cache-assets')

module.exports = async function () {
  return Cache(
    'https://ghibliapi.herokuapp.com/films/',
    { type: 'json' }
  )
}

USING Dynamic DATA

---
layout: base
title: Studio Ghibli movies
---

<h1>Studio Ghibli movies</h1>

<ul>
  {% for movie in movies %}
    <li>
      <a href="/movie/{{ movie.title | slug }">
        {{ movie.title }}
      </a>
    </li>
  {% endfor %}
</ul>

PAgination API

  • Allows to split collections into multiple pages
  • But it can also be used to generate pages for dynamic content
  • For instance, a page for every single movie

PAgination API

---
layout: base
permalink: /movie/{{ movie.title | slug }}/
pagination:
  data: movies
  size: 1
  alias: movie
---
<h2>{{ movie.title }}</h2>

<ul>
  <li>Released in <strong>{{ movie.release_date }}</strong></li>
  <li>Directed by <strong>{{ movie.director }}</strong></li>
  <li>Produced by <strong>{{ movie.producer }}</strong></li>
</ul>

<p>{{ movie.description }}</p>

<p><a href="/movies">See all movies</a></p>

BONUS! Automate deployment

Example using GitHub actions:

loige.link/11ty-ci-example

Thanks!

If you enjoyed this talk, you might also enjoy nodejsdp.link 😛

Let's connect!

  loige.co (blog)

  @loige (twitter)

  lmammino (github)