eleventy
eleventy copied to clipboard
passing data from the page to the base layout
The subject says it all. How can one pass data from the page to the base layout?
It seems like there are a couple of issues open/closed talking about this already, but it would be nice to get an authoritative answer (and some docs in place) on how do tackle such a common task. Here is a test case
layout/test-base.njk:
<!doctype html>
<html>
<body>
VAR=[{{ header }}]<br>
[BASE]{{ content | safe }}[/BASE]
</body>
</html>
layout/test-next.njk:
---
layout: layouts/test-base.njk
---
{% set header = "next" %}
[NEXT]
{{ content | safe }}
[/NEXT]
test-page.njk:
---
layout: layouts/test-next.njk
---
{% set header = "page" %}
PAGE
What I found to be working is setting it in the frontmatter.
test-page.njk:
---
layout: layouts/test-next.njk
header: page
---
PAGE
But this fails short as soon as you want to include a bit more along the lines of
{% set header %}
{% include "./foo" %}
{% endset %}
Thanks for the issue! Reposting from our convo in Discord.
It looks like what you're trying to do is more complex than making data available in the layout chain, it's looking for deeper integration of the layout chain and specific templating features of Nunjucks. Can you confirm this is something Nunjucks can do on its own outside of 11ty? If so, can you try this without using the built in layout functionality of 11ty, but just Nunjucks directly in 11ty?
@tcurdt I don't think you can do this - or at least I've not found a way. I can only get data from frontmatter in my parent template, not anything I define in the page template.
One option is to bypass Eleventy layouts entirely - just have your template call extends to whatever template. I think this has the downside that you can't use frontmatter though.
What I've done (which isn't ideal) is in my page template set something in the frontmatter as a marker, and then have my parent template look for that marker and do something different because of it.
@Snugug While I am using nunjucks for now, this should be a general question and covered in the docs as such. Pushing data from the page into the header of a html page is not something extraordinary. While often passing strings through the frontmatter is good enough, a non-ugly way of passing a full file would be more than nice. It seems everyone comes up with his/her own (ugly) workaround for it. It would be nice to have clear and easy path forward for this.
@edwardhorsford I also tried the extends path. The frontmatter work fine with this. But I am not sure whether this a nunjucks or an integration problem, with 11ty I cannot define a block in the page - only in the layout. If that was working all would be fine.
Another work around that seems to work OKish is to pass the file names via frontmatter.
So in the base layout I have:
{% for path in head %}
{% include page | relative(path) %}
{% endfor %}
And in the page I can set
---
layout: base
head:
- _some.inc.html
---
The important bit is to find the file from the base layout.
config.addFilter("relative", (page,file) => {
return "../../../" + path.dirname(page.inputPath) + "/" + file
})
This is way more ugly than it should be - but it works for now.
@tcurdt that's a clever workaround!
@edwardhorsford thanks :)
While I think I am fine with this approach (for now), I think it would be good somehow to mention this topic in the docs. Injecting values into the page header is a common requirement. The options should be better covered instead of being hidden in an github issue.
I was surprised to find that variable setting didn't work. I thought I saw that some other place working. And it's a real shame that the nunjucks block approach does not work. Although I believe that's just a bug to find and fix. On the other the other hand the above workaround is not template engine specific. Which is nice, too.
https://github.com/11ty/eleventy/issues/1397#issuecomment-690145975 is how I declare dependencies on JS and CSS files for articles :-)
I need this and this is what I have come up with
base.njk
{% if page.foo %}
<div class="foo">
{{ page.foo }}
</div>
{% endif %}
The page:
---
layout: base
---
{% setPageVar 'foo' %}
some foo content
{% endsetPageVar %}
.eleventy.js
config.addPairedShortcode("setPageVar", function(content, name) {
this.page[name] = content;
return '';
});
Can use other object to avoid var name collision with the page object.
Just circling back here, {% set %} does not feed into the data cascade. It’s scoped to the current page only!