blog

Three ways to keep drafts in 11ty

This blog is made using Eleventy base blog. It’s a pretty simple setup. I write my posts in markdown and use Nunjucks for templating. One feature that was missing was the ability to keep unpublished blog posts as drafts. I found ~two~three ways to add that in.

Filter posts using metadata

In my front matter data I added draft: true so it looks like this:

---
title: Two ways to keep drafts in 11ty
date: 2021-12-07
draft: true
tags:
- dev
- js
---

And I can use it in my blog.njk template that loops over all posts in a collection and lists them on /blog page.


<div class="postlist">
{% for post in postslist | reverse %}
{% if not post.data.draft %}
<article class="postlist__item">
{# Title, link etc. goes here #}
</article>
{% endif %}
{% endfor %}
</div>

The {% if not post.data.draft %} conditional is used to filter out posts marked as drafts. Note that this doesn’t actually stop the post from being published and it will still be available via direct link.

Filter posts using date

This approach is better if I have a finished post but wanna delay publication. But I can also set some crazy future date like the year 2089. while I’m working on an article and just change it back to the current date when ready.

I can do that by adding a custom filter in my .eleventy.js to check if the post date is set in the future.

eleventyConfig.addFilter('isPublished', function(post) {
return post.filter(post => new Date(post.date) <= new Date());
});

And now I can use it to filter out my posts like so:


<div class="postlist">
{% for post in postslist | reverse | isPublished %}
{% if not post.data.draft %}
<article class="postlist__item">
{# Title, link etc. goes here #}
</article>
{% endif %}
{% endfor %}
</div>

Here, I apply the filter directly on my post list collection (the {% for post in postslist | reverse | isPublished %} part). In fact, let’s convert the draft conditional into a filter as well to clean things up.

First, add another filter to .nunjucks.js:

eleventyConfig.addFilter('notDraft', function(post) {
return post.filter(post => !post.data.draft);
});

Second, update blog.njk template to look like this:


<div class="postlist">
{% for post in postslist | reverse | isPublished | notDraft %}
<article class="postlist__item">
{# Title, link etc. goes here #}
</article>
{% endfor %}
</div>

Now I have the ability to mark posts as drafts AND schedule publishing in the future! Note that neither of those stops my articles from getting processed and available from a direct link.

From John’s article I lerned about permalink: false key. I can add it to my front matter to make sure no drafts get built and published! This can be combined with both approaches listed above but note that in case of removing permalink, scheduling posts wouldn’t work.

Use a separate directory

Create a folder like _drafts in the project root, add it to .eleventyignore and just keep your drafts there until ready.

This way nothing unfinished gets published and you don’t have to worry about direct links. It’s also the fastest since eleventy doesn’t have to go through all drafts and filter them out. What’s missing is the ability to easily preview the post locally.

Note about local development

Again, John Nemp-Cruz has a good article on 11ty drafts and how to set up visibility depending on enviroment. It a bit much for my needs so I still rely on manually changing front matter keys.