Edit on August 19, 2021: I found an issue with my code where a shortcode for 'foo' on page 1 would be shared with the same name on other pages. I corrected it by using the current page scope. Fixes are inline.

Ok, so let me start off by saying that a) I'm not sure this is a good idea and b) it may already exist and I just don't know about it. This all came about from me doing some research on an Eleventy tagged question on StackOverflow. If you aren't aware, Liquid has a tag built in called capture. It looks like so:


{% capture my_variable %}
I am being captured.
{% endcapture %}

This then lets you output my_variable. Having paired shortcodes like this makes it easier to capture dynamic output and save it to a variable. So for example:


{% capture my_variable %}
I am being captured at {{ "now" | date: "%Y-%m-%d %H:%M" }}.
{% endcapture %}

One interesting aspect of the capture shortcode though is that it always sets the value to what you capture. If you had something in that variable already, it gets overwritten. I think that's expected and not bad, but here's an example of that as well:

{% capture my_variable %}I am being captured.{% endcapture %}
{% capture my_variable %}MORE captured.{% endcapture %}

If you output my_variable, you will only get MORE captured. Again, I think this is expeted. But it got me thinking - what if we built a shortcode that appended, rather then replaced, content? This is what I came up with:

module.exports = function(eleventyConfig) {

	let _CAPTURES;
	eleventyConfig.on('beforeBuild', () => {
		//I need this to wipe _CAPTURES when editing pages, wouldn't be an issue in prod
    	_CAPTURES = {};
	});
	
	eleventyConfig.addPairedShortcode("mycapture", function (content, name) {
		if(!_CAPTURES[this.page.inputPath]) _CAPTURES[this.page.inputPath] = {};
		if(!_CAPTURES[this.page.inputPath][name]) _CAPTURES[this.page.inputPath][name] = '';
		_CAPTURES[this.page.inputPath][name] += content;
		return '';
	});

	eleventyConfig.addShortcode("displaycapture", function(name) {
		if(_CAPTURES[this.page.inputPath] && _CAPTURES[this.page.inputPath][name]) return _CAPTURES[this.page.inputPath][name];
		return '';
	});

};

This .eleventy.js file defines two shortcodes - mycapture and displaycapture. I define a global variable (I'll explain beforeBuild in a sec) named _CAPTURES that stores key value pairs. In order to keep a key, foo, local to one page, I use the current page's inputPath value. (This is something I edited after the initial blog post.) When using mycapture, the text inside the shortcode get passed to the content variable and when I actually write the shortcode, I include the name argument. Here's an example:

{% mycapture "foo" %}
<p>
This is test i think 1
</p>
{% endmycapture %}

{% mycapture "foo" %}
<p>
This is test i think 2 
</p>
{% endmycapture %}

Here I've captured "foo" twice. And then to output it, I do:

<p>
And here is my demo, should show two parts:
    {% displaycapture "foo" %}
</p>

And that's it. Using the sample above you get:

<p>
This is test i think 1
</p>
<p>
This is test i think 2
</p>

So one thing weird I noticed is that the content began to duplicate itself. So instead of two paragraphs, I'd had four. From what I could gather, Eleventy was not rerunning .eleventy.js on me editing a page, so it didn't clear the variable. I initially had:

const _CAPTURES = {};

I kept getting inconsistent results that would go away if I killed the Eleventy CLI and ran from scratch. I finally figured out what happened and that's when I added the beforeBuild event. In theory it's not needed in production as you aren't refrefshing there, but it doesn't hurt being there as is I think.

If you want a copy of this, you can find it here: https://github.com/cfjedimaster/eleventy-demos/tree/master/additive

Photo by Jakob Owens on Unsplash