eleventy
eleventy copied to clipboard
What is the best way to pass a block from a layout to a parent layout?
I'm using Nunjucks layouts, and the layout
attribute in Front Matter for layout chaining, so that a page.njk
layout uses a base.njk
layout.
As I read it, the Addendum about existing Templating features states that “Eleventy’s layout system exists a layer above Nunjucks' Template Inheritance exposed with {% extends %}
“.
So I thought {% extends %}
was implicit when using layout chaining, but a {% block %}
defined in my page.njk
layout never arrives into base.njk
.
I tried to add an explicit {% extends %}
like I saw in Phil Hawksworth's code, but I get the base.njk
included twice, kind of recursively
If I remove the layout
attribute from the Front Matter of the page.njk
layout, its content doesn't arrive into base.njk
anymore.
I must be missing something obvious, so how is it supposed to work?
Thanks
If I recall correctly, in Eleventy, Nunjucks happily passes content through to the specified layout without using extends
if you are just populating the content
. When you have multiple blocks that you wish to pass through to a layout, then you need to use extends
.
At least, this seems to work for me™
I'm struggling to find the documentation I thought had lead me to that conclusion.
@philhawksworth what I see in your aforementioned post.njk
layout is that you have an explicit {% extends … %}
, but no Front Matter and no content outside {% block … %}
s.
So it looks like it's either one or the other:
- Eleventy layout chaining with Front Matter
layout
property OR - an
{% extends … %}
and a series of{% block … %}
s
The later seems to be the best (or even the only way) to "pass" an HTML fragment (a "block") to the parent (extended) layout.
Oh, looks like @zachleat already answered this is "by design": https://github.com/11ty/eleventy/issues/834#issuecomment-569474008
That's not why I understood when reading the Addendum about existing Templating features, maybe we could rephrase it.
Aha. Yeah, there it is. Nice one @nhoizey!
Default support for blocks would be super nice.
In the mean time, I've set up these tags to recreate similar functionality, maybe it's of help to someone else.
module.exports = function (eleventyConfig) {
const layoutblocks = [];
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.page.layoutblock || {})[name];
});
eleventyConfig.addPairedShortcode('layoutblock', (content, name) {
if (!this.page.layoutblock) this.page.layoutblock = {};
this.page.layoutblock[name] = content;
});
}
Define a layoutblock
block with name foo
like this in the layout template:
{% renderlayoutblock 'foo' %}
Set the contents of layoutblock
with name foo
in a document like this:
{% layoutblock 'foo' %}
Hello World
{% endlayoutblock %}
@rikschennink Thanks for the suggestion! this.page.layoutblock
doesn't work because this.page
is undefined
. And the variable const layoutblocks = []
is obsolete as it's not used. But this worked for me:
module.exports = function (eleventyConfig) {
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.layoutblock || {})[name];
});
eleventyConfig.addPairedShortcode('layoutblock', (content, name) {
if (!this.layoutblock) this.layoutblock = {};
this.layoutblock[name] = content;
});
}
@gluecksmensch good one.
Previous iteration I stored data in const layoutblocks = []
but that didn't work ( obviously :D ) forgot to remove.
I guess with my templates page
is defined, didn't realise this wasn't always the case, nice improvement 👍
@rikschennink @gluecksmensch as we showed before in the thread, you can use Nunjucks' {% extend … %}
directly, without additional shortcode, but you can't use Eleventy's layout
Front Matter with it.
I do this here for example: https://github.com/nhoizey/precious-prana.com/blob/master/src/_includes/layouts/evenement.njk#L2
And here's the base.njk
layout definig blocks:
https://github.com/nhoizey/precious-prana.com/blob/master/src/_includes/layouts/base.njk
11ty newbie here. I hit this same issue, and followed the pattern from @nhoizey, however all the block sections are missing in the generated html.
index.njk:
{% extends "layout.njk" %}
{% block title %} Home page in HTML {% endblock %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>
Welcome to my 11ty website.
</p>
{% include "footer.njk" %}
{% endblock %}
_includes/layout.njk
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<style>
.active { background: yellow }
</style>
{{ header | safe }}
</head>
<body>
<div>
<a href="/" class="{{ 'active' if '/' == page.url }}">Home</a>
<a href="/README" class="{{ 'active' if '/README/' == page.url }}">README</a>
</div>
{{ content | safe }}
</body>
</html>
Generated index.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
.active { background: yellow }
</style>
</head>
<body>
<div>
<a href="/" class="active">Home</a>
<a href="/README" class="">README</a>
</div>
</body>
</html>
Full source code is here: https://github.com/surferjeff/scratch/tree/main/hello-11ty
@surferjeff Looking at @nhoizey's repo, I think you need to define your blocks in your _includes/layout.njk file, similar to this:
https://github.com/nhoizey/precious-prana.com/blob/83380d5a9e489d1e4c25c4077c68ac2461bc16a4/src/_includes/layouts/base.njk?rgh-link-date=2021-02-22T09%3A38%3A39Z#L27-L30
- <title>{{ title }}</title>
+ {% block title %}
+ <title>{{ title }}</title>
+ <meta property="og:title" content="{{ title }}" />
+ {% endblock %}
Then you can either set the title
via front matter, or override it by explicitly setting the {% block title %}
block:
{% block title %}
<title>Precious Prana, faire pétiller</title>
<meta property="og:title" content="Precious Prana, faire pétiller" />
{% endblock %}
(see https://github.com/nhoizey/precious-prana.com/blob/f7edfa0b1da0e36e9c52115dd2228a6e9629e899/src/_includes/layouts/homepage.njk#L3-L6)
@surferjeff Try something like this:
<!DOCTYPE html>
<html>
<head>
{% block title %}
<title>{{ title }}</title>
{% endblock %}
<style>
.active { background: yellow }
</style>
{% block header %}
{{ header | safe }}
{% endblock %}
</head>
<body>
<div>
<a href="/" class="{{ 'active' if '/' == page.url }}">Home</a>
<a href="/README" class="{{ 'active' if '/README/' == page.url }}">README</a>
</div>
{% block content %}
{{ content | safe }}
{% endblock %}
</body>
</html>
And…
{% extends "layout.njk" %}
{% block title %}
<title>Home page in HTML</title>
{% endblock %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>Welcome to my 11ty website.</p>
{% include "footer.njk" %}
{% endblock %}
Or set the title
via front matter, if you don't want to set the <title>
tags and override other stuff:
---
title: Override title
---
{% extends "layout.njk" %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>Welcome to my 11ty website.</p>
{% include "footer.njk" %}
{% endblock %}
Ah, I see now. Thanks for the quick replies!