BTrem

microblog with eleventy

I use 11ty to publish articles like this one, but I also want to be able publish status updates, sort of like my own Twitter feed. Like a Twitter feed, I want each update to include a date and time when I wrote it. Unlike a twitter feed, I don't want each post to have a permanent url. Instead, I want to show the most recent updates on my home page, and I want to bump off the oldest status update every time I add a new one. This article explains how I did it.

Before I began, I had to decide what a status update should include. For the time being, I want to keep things very simple: a date and time, and a sentence or two saying what I'm up to. Since status updates will be simple, I can use a simple format: markdown. A markdown (.md) file can be a paragraph, several paragraphs, a list, and 11ty will convert it into html markup for me. It can do the same with a simple note that consists of only a sentence, which is often the case for microblogging. And I change on a per-post basis. So if I need something more advanced for a complicated note, I can write it in Nunjucks syntax, giving me lots of markup flexibility.

The other thing I need for my updates is the date and time. 11ty helpfully uses the file creation timestamp to set the default date for a post. I can override it in front matter for an individual posts, but for simple updates, I probably won't need to.

With that settled, here's what I did to create my status update content with 11ty:

create a new directory for my status updates

First, I created a sub-directory inside src/, my 11ty input directory. I named the new directory status.

turn off html file generation

While each post I write will be contained in a file in my input directory, I didn't want 11ty to make individual html files in my output directory. So I needed to turn off permalinks for those files. Instead of doing that in each file's front matter, I did it for the entire directory with a special data file. In my newly created /status/ directory, I created a file and named it status.11tydata.json. Note that the root name, status, matches the directory name. In that json file, I added the following:

{
"permalink": false
}

That will be the default setting for all files in the status directory; it's the equivalent of adding permalink: false to each file's front matter section. Even though 11ty won't create any files based on my status update posts (nor links in any navigation lists), it will still process the file, making it available to collections.

creating a collection of status updates

That brings us to the next step: creating a collection of status update posts in the .eleventy.js configuration file. I could have made my collection based on a tag, but then I would have had to add that tag to every status update post. Instead, I used getFilteredByGlob, which gets posts by directory. Here's what I added to .eleventy.js:

eleventyConfig.addCollection('status', function(collectionApi) {
return collectionApi.getFilteredByGlob('src/status/*.*');
});

That gets all status updates posts, automatically sorted by date.

(The .eleventy.js configuration file that I created for this tutorial is shown at the bottom of this article in the addendum.)

add the status update posts to the website

Now I was ready to add the status updates to my site. I use Nunjucks as my 11ty template engine. Nunjucks has a built-in for loop that processes a collection one post at a time. I wanted to put each status update in an article element, with the date of the post in a time element. Here it is in action, from my home page template src/index.njk:

<section class="statusUpdates">

<h1>what I'm up to at the moment</h1>


{% for item in collections.status) %}
<article>
<header>
<time datetime="
{{ item.date.toISOString() }}">
{{ item.date }}
</time>
</header>

{{ item.templateContent | safe }}
</article>

{% endfor %}

</section>

The post is referenced as a javascript object. So the date is shown as an attribute: item.date. Note that, because it's javascript, I can use javascript methods like toISOString() to supply a machine readable date for the the datetime attribute of the time element.

The content of the post is item.templateContent. There is a buiit in nunjucks filter I'm applying, safe, so that the html is rendered properly. If you want to know the basics of templates and filters, take a look at the 11ty page on layouts.

fine tuning the updates list

reverse the order of updates

That will show all status updates in chronological order. But I want to show the newest status update at the top. To do that, I added a built-in nunjucks reverse filter to my for loop. Right after the collections.status variable, I inserted a pipe character ("|"), then the filter name, reverse. The reverse filter is a function that takes one parameter, the part that comes before the pipe character. In my case, that variable is an array of status update posts, which the filters returns in reverse order. Here's my updated code:

<section class="statusUpdates">

<h1>what I'm up to at the moment</h1>


{% for item in collections.status | reverse) %}
<article>
<header>
<time datetime="
{{ item.date.toISOString() }}">
{{ item.date }}
</time>
</header>

{{ item.templateContent | safe }}
</article>

{% endfor %}

</section>

limit the number of status updates in the list

The only problem now is that, as the number of status updates grow, so will my home page. I want to limit the number of posts that 11ty will process. For that I have to create a custom filter and add it to my .eleventy.js configuration file. Recall from above that a filter is a function that takes a parameter. The parameter I want to limit is a collection of posts. Since a collection is an array, I can slice the array and return the slice. Here's what I added to .eleventy.js:

eleventyConfig.addFilter('limit10', function(arr) {
return arr.slice(0, 10);
});

(Again: the .eleventy.js configuration file that I created for this tutorial is shown at the bottom of this article in the addendum.)

You can chain filters by adding them to the statement, each separated by a pipe character:

<section class="statusUpdates">

<h1>what I'm up to at the moment</h1>


{% for item in collections.status | reverse | filter %}
<article>
<header>
<time datetime="
{{ item.date.toISOString() }}">
{{ item.date }}
</time>
</header>

{{ item.templateContent | safe }}
</article>

{% endfor %}

</section>

Now 11ty will get the collection of status update posts; reverse their order; slice it so that only the most recent ten are left; and finally run a loop, assigning the posts one by one to the item variable. In each loop, it will output the post's date and the content in standard html markup for display on my home page.

a small bit of style

To separate updates, I added a border between each item by adding the following code to my site's stylesheet:

.statusUpdates > article {
border-top: 1px solid gray;
}

adding my first status update

I just created a new file in my status directory called microblog-launch.md with just eight words:

I just created a personal twitter-like microblog.

And voilà, on my home page, under the headline "what I'm up to at the moment" is my first status update.

a screenshot of my home page, showing my first status update

what this doesn't do

This is not really micro-publishing, in the sense that I have to create a file with the note. So I have to be at my laptop to enter a status update. In the future, I might try to figure out how I might update my status from a mobile device, perhaps using a form of some sort.

If I do that, I might add the ability to include a location when I'm posting from somewhere other than home. Or not. I do value my privacy.

why not just use Twitter?

You might be wondering, why not just open a Twitter account? Because I want more control over where, when, and how to publish my thoughts. Plus, I really hate Twitter. Not as much as Facebook, but still. Not a fan.

addendum: the configuration file (edited)

Here is the .eleventy.js configuration file to go along with this article:

module.exports = function(eleventyConfig) {

eleventyConfig.addFilter('limit10', function(arr) {
return arr.slice(0, 10);
});

// Filter status update file names using a glob
eleventyConfig.addCollection('status', function(collectionApi) {
return collectionApi.getFilteredByGlob('src/status/*.*');
});

// 11ty defaults
return {

dir: {
input: 'src'
}

};
};