Creating Filters, Shortcodes and Plugins

Filters, shortcodes and plugins are among the most powerful features in 11ty. Let's understand the concepts and learn how to create them.

This is the 8th post of the series - building personal static site with 11ty. Here is the GitHub Repo jec-11ty-starter if you prefer to read the code first.

Filters

Every blog post has a date. I stored the date in ISO date format yyyy-MM-dd (e.g. 2020-12-31). However, I would like to display it in MMM dd, yyyy (e.g. Dec 31, 2020) format because I find that more readable. (Scroll to the top of this post to see the date in action)

Filters offer us a way to process our data and output it to a format we want. In our case, we will be creating a date format filter. Let's name it dateDisplay. (naming is hard, couldn't figure out a better name yet. 😂)

// output: Dec 31, 2020

{{ '2020-12-31' | dateDisplay }}

The syntax above shows how we will use the filter in our code. Next, let's write the dateDisplay filter function!

// configs/date-display.filter.js

const { parseISO, format } = require('date-fns/fp');

module.exports = (input) => {
const formatter = format('MMM dd, yyyy');
const date = input instanceof Date ? input : parseISO(input);
return formatter(date);
};

The code is quite expressive itself. The input could be a date or a string in ISO format. We are using the date-fns package to format the date. Install it by running npm install date-fns -D.

Now we have our function ready, but we have not told 11ty that this is a filter. Let's configure that in our .eleventy.js file.

// .eleventy.js

// add these 2 lines
const dateDisplay = require('./configs/date-display.filter');
eleventyConfig.addFilter('dateDisplay', dateDisplay);

Shortcodes

I embedded Youtube videos in almost all my presentation posts - jec.fyi/deck. The code to embed the Youtube video, as copied from Youtube, would be something like this:

<iframe src="https://www.youtube.com/embed/[your-video-id]"
frameborder="0" allowfullscreen
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture">

</iframe>

It is okay for us to write (or copy paste) these code manually to each page with YouTube video. However, it's repetitive. Also, it might be harder to apply changes in the future, for example:

It would be good if we can shorten the code and centralize the changes. Shortcodes could help us with that.

// output: <iframe ...></iframe>

{% youtube 'your-video-id' %}

The code above shows how's a shortcode syntax looks like. We will create a youtube shortcode function which accepts a video id. The output would be the HTML iframe.

Here is how our function looks like:

// configs/youtube.shortcode.js

const outdent = require('outdent')({ newline: ' ' });
module.exports = (id) => {
return outdent`
<div class="video-wrapper">
<iframe src="https://www.youtube.com/embed/
${id}"
frameborder="0" allowfullscreen
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture">
</iframe>
</div>
`
;
};

The code is fairly self-explanatory. We wrap the iframe with additional div to style the YouTube iframe. We use the outdent function (install it with command npm install outdent -D) to format the output string, replacing the new lines with space.

The reason we use outdent is because the way 11ty processes new lines is not what we expected - refer to the Eleventy explanation here. If you prefer to not install the outdent package, just rewrite your code above (all the HTML) in a single line. I don't like it because it is not readable during development.

Here is the CSS to style our video-wrapper, to make our YouTube video display responsively (refer to this article for details.). I placed this css in src/_includes/writing.layout.css because that's where my video lives in, but you can place it in any css file with video.

/* Put it in any css file with video */

.video-wrapper {
position: relative;
padding-bottom: 56.25%;
}

.video-wrapper iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}

Great, now we have the function ready, configure that in .eleventy.js so we can use this shortcode in our template.

// .eleventy.js
eleventyConfig.addShortcode(
'youtube', require('./configs/youtube.shortcode'));

I use the same concept to create speakerdeck, img and twitter shortcodes on this website.

Plugins

The filters and shortcodes that you've created might be useful for others. You might want to reuse it in other projects or just publish it in npm so other people can benefit from it.

We can create them as a plugin. Let me show you how we can make our date-display filter and youtube shortcode a plugin instead. 😃

First, create a folder plugins and an index.js file in it.

# folder structure
- plugins
- index.js # new file

Next, move the filter and shortcode to the plugins folder instead.

# folder structure
- plugins
- index.js # new file
- date-display.filter.js
- youtube.shortcode.js

Remove the code filter and shortcode configuration we added in .eleventy.js just now. Save it. Try to run npm start now, you will hit an error (something like "unknown block tag: youtube"). We will fix this in a moment. 😉

Open our plugins/index.js file. Here is the code.

// plugins/index.js

module.exports = function(eleventyConfig) {

eleventyConfig.addFilter(
'dateDisplay',
require('./date-display.filter')
);

eleventyConfig.addShortcode(
'youtube',
require('./youtube.shortcode')
);
};

Did you notice that this plugin code is similar to our .eleventy.js configuration file (except the differences in require file paths)? It is because the theory of creating plugins is similar to .eleventy.js configuration.

Now, let's update our .eleventy.js to use our plugins.

// .eleventy.ts
eleventyConfig.addPlugin(require('./plugins'));

Try to start your application again. The errors are gone. Date and YouTube video are shown correctly on screen.

Splitting your code into plugins in the same projects might not be very useful. However, if you need the same set of filters and shortcodes across multiple projects, you can export them as npm packages and reuse them across projects.

In fact, we can also install some plugins that others have published! Here is a list of community created plugins.

These are two plugins I used in this website:

Alrighty, what's next?

Yay! We've learnt how to create filters and shortcodes, and how to convert them into plugins to make it reusable across projects.

Here's the GitHub repo for the code above: jec-11ty-starter. I'll update the repo whenever I write a new post.

In the coming posts, I plan to write about more on how I built my website with 11ty:

Let me know if the above topics interest you.

That's all. Happy coding!

Have something to say? Leave me comments on Twitter 👇🏼

Follow my writing:

Hand-crafted with love by Jecelyn Yeen © Licenses | RSS Feed