Eleventy(11ty) + Strapi CMS with dynamic page creating





5 min read


Sep 22, 2019





2 open source projects, both looking very promising can actually work well together.

Eleventy / 11ty: https://github.com/11ty/eleventy/
Strapi: https://github.com/strapi/strapi

We’ll create a page with list of services, each linking to a page describing the service. Strapi will be our API. Content we create and save there should automatically reflect on our “static” services page after a refresh.

📹 Demo of the final outcome: https://i.imgur.com/qylkFtA.mp4

Here’s our plan:

  1. Setup Strapi and get JSON for our services list.
  2. Create a simple static list of services on Eleventy.
  3. Setup auto-build with Strapi hooks to rebuild our static Eleventy site with fresh content.

At the time of writing I’m using the following:

Node v11.15.0
Eleventy v0.9.0
Strapi v3.0.0-beta.16.5
OS: macOS

Before getting started we need npm (for Eleventy) and yarn (for Strapi).
There might be a way to install all that’s needed for Strapi with npm not yarn, but my attempts on it were unsuccessful.

1. Setup Strapi and get JSON for our services

To keep things simple to maintain, we’re going to keep our Strapi API and Eleventy is separate folders.

Create 2 folders:

Follow the getting started instructions from Strapi documentation until you have setup an admin account. Make sure to setup Strapi in your strapi-api folder: https://strapi.io/documentation/3.0.0-beta.x/getting-started/quick-start.html

If you see admin dashboard of Strapi, great! Go ahead and create a new content type via Content Type Builder called service

Add following fields to it:
Rich text called text
Text called title
Text called **slug
**When creating new services later the slug field should be filled with lowercase string, dashes instead of spaces (ex.my-service-one). This will be service url on your static page.

Save your new content type, wait for Strapi to restart and you should see Services in the menu of Strapi under content types section. Open that up and create a few services.


If after creating our services we navigate to http://localhost:1337/services, we will get 404 Not Found. This is because by default all new content types are private, meaning we need to setup public permissions for it first.

Back to Strapi menu, look for Roles & Permissions.

Click on Public, look permissions of services (bunch of checkboxes). Tick the box with “find”. Save permissions, wait for Strapi to restart.

With the permissions set try go to http://localhost:1337/services, you should be looking at JSON of the services you had created in the panel.

2. Create a simple static list of services on Eleventy

Switching to our eleventy-starter folder, let’s start by entering following commands:

npm init -ynpm install --save-dev @11ty/eleventy

Open package.json and add a build script

..."scripts": {
  "build": "eleventy --quiet"

Next we’ll create a file called 11ty.js. This is where the real magic will happen

const { spawn } = require('child_process');
const fetch = require('node-fetch');
const fs = require('fs');
const express = require('express');
const app = express();
const PORT = 3005;
const STAPIURL = `http://localhost:1337`;
const services = async () => {
   await fetch(`${STAPIURL}/services`)
      .catch(err => {
      .then(res => res.json())
      .then(services => {
         console.log(`Found ${services.length} service(s)`);
         for (const service of services) {
            const {title, text, slug} = service;
            const content = `--- \ntitle: ${title}\ntags: service\n--- \n${text}`;
            try {
               const filename = `${slug}.md`;
               console.log(`Writing ${filename}`);
               fs.writeFileSync(filename, content)
            } catch (err) {
const init = async () => {
   await services();
   let watcher = spawn('npm',['run','build']);
   watcher.stdout.on('data', (data) => {
   app.get('/reload', async (req, res) => {
      await services();
      console.log('Rebuilding Eleventy');
      watcher = spawn('npm',['run','build']);
      watcher.stdout.on('data', (data) => {
      return res.sendStatus(200);
   app.listen(PORT, () =>
      console.log(`Example app listening on port ${PORT}!`),

Now this does couple of things:

  1. Requests all our services from Strapi API, creates markdown files out of those and saving them in your eleventy-starter folder.
  2. Builds your template (we’ll create one later) & markdown files into HTML files that end up in _site folder.
  3. Setup a node express server on port 3005, serving your static site and accepting GET requests on /reload path to rebuild your static files (more on that in step 3).

Before we run 11ty.js, there is a Nunjucks template file we should create which will show list of all services with links to them.

At the time of writing this, Nunjucks and Liquid template languages seem to have the best support for everything Eleventy has to offer.

Create a file called index.njk

<!doctype html>
    {%- for service in collections.service -%}
        <li><a href="{{ service.url }}">{{ service.data.title }}</a></li>
    {%- endfor -%}

Everything set?
Let’s run that 11ty.js file

node 11ty.js

If everything went right, markdown(.md) files should appear in your folder and a express server running on port 3005, check it out http://localhost:3005/

3. Setup auto-build with Strapi hooks to rebuild our static Eleventy site with fresh content.

Remember the /reload GET request we had setup via express earlier? Here’s where we will use it.

Strapi Hooks provide us callback methods where we implement our reload requests.

Make following changes to config/environments/development/custom.json

Keep in mind that this is for development environment only, if you want to apply this in production look for the production custom.json config file.

  "myCustomConfiguration": "This configuration is accessible through strapi.config.environments.development.myCustomConfiguration",
  "staticWebsiteBuildURL": "http://localhost:3005/reload"


'use strict';
const axios = require('axios');
const sendReload = () => {
      .catch((e) => console.log(e));
module.exports = {
   afterSave: async () => sendReload(),
   afterCreate: async () => sendReload(),
   afterDestroy: async () => sendReload()

After you save this, Strapi should restart itself and any further changes to entries in Services should make request to http://localhost:3005/reload, rebuilding our static site with new data.

All you need to do now after updating content in Strapi services is refresh your static site and all your changes should show up.

Ideally when we delete a entry from our Services in Strapi, markdown file locally should also be removed. This is not implemented yet in our 11ty.js file and will not be covered within this article.

Hopefully you made through just fine, toodles!