Use Eleventy to generate a Ghost blog

Eleventy is swiftly becoming the most popular static site generator within my online feeds. Check out the site if you want to know more. If you’re a fan of Jekyll, you’ll probably like Eleventy just as much…maybe more.

Last night, I had a thought, which was only perpetuated by Andy:

What if I could source a Ghost blog from the Ghost Content API and then generate a static blog, all inside of Eleventy?


You’re going to need Eleventy installed for this in some shape or form - check out the docs to get setup.

Data sourcing

Creating and getting data in Eleventy builds upon the method that Jekyll already uses. A _data/ directory is where you can store chunks of data that are then exposed in your template files. Eleventy takes this one step further, though, with the ability to use JavaScript files to source and create data structures.

I guess, if we can use JavaScript, then we can probably use some libraries, too? Let’s have a go by installing the Ghost Content API Client Library:

npm install @tryghost/content-api --save-dev

Then a data file - in this case, the file is called _data/posts.js:

// The library that was just installed
const contentAPI = require('@tryghost/content-api');

// New instance with Ghost demo API credentials
const api = new contentAPI({
  url: '',
  key: '22444f78447824223cefc48062',
  version: 'v2'

// Export data called from the API
module.exports = async function() {
  return api.posts.browse()
      .catch((err) => {


This clever API code is all well and good, but you’re not going to see anything without some template files to present content. I’ve provided a very raw example below using nunjucks templating, called index.njk. Note that the variable posts is the same as the name given to the data file. This is part of how Eleventy passes that data to the template files:

  {% for post in posts %}
       <a href="/posts/{{ post.slug }}/">
         {{ post.title }}
  {% endfor %}

Let’s run Eleventy with eleventy --serve and see what happens:

Unformatted list of linked post titles

What a beautifully designed blog 💅. Joking aside, it worked! We sourced content directly from the Ghost API and generated a little static blog with Eleventy! 🎸

Rendering single posts

This blog isn’t quite a blog, though; we’re linking each post to an optimistic, but disappointing, 404 page. I guess we could link to the original post on a Ghost site, but that’s not a real static blog, right?

Oddly, this part of my personal challenge was what I got stuck on the most. The creation of single pages for chunks of content is wrapped up in ‘Pagination’. On the face of it, this sounds weird. Why would I want to use pagination, something that creates groups of things, to create the one thing?

If you think about it though, it’s not that far off. Pagination is where you split a long array of items into smaller chunks. Well, what if you wanted to split those chunks to a small enough size that each page was just one thing? That’s pretty much what we want: a single page with one item on each.

Again, we’re going to use nunjucks for templating, but enlist some frontmatter to configure our pagination:

  data: posts
  size: 1
  alias: post
permalink: posts/{{ post.slug }}/
<h1>{{ post.title }}</h1>
{{ post.html | safe }}

Let’s break it down…

I’ve called this file post.njk, but since it’s never outputted, it doesn’t really matter.

All the post files generated, as well as the project files
Example post in the browser

We’re done! An Eleventy static site sourcing content from the Ghost API.

This doesn’t just work for the Ghost API. With the JavaScript fetch API, we can pull in content from all sorts of places.

How about all your repos on GitHub?

const fetch = require("node-fetch");

module.exports = async function() {
  const data = await fetch('');
  const json = await data.json().then(data => data);
  return json;

This feature alone makes Eleventy a really powerful tool. Have a play and let me know what you make with it.