Harnessing Eleventy’s Data Cascade: A Practical Guide to Inherited Page Context
Eleventy’s data cascade automatically merges JSON, YAML, and JavaScript data files from a page’s directory and all ancestor directories. This blog walks through the cascade, shows a concrete example of inheriting navigation and author data, and discusses trade‑offs like build‑time evaluation.
11 Oct 2026, 22:56 UTC

Problem: Repetitive Site‑Wide Data Across Pages
When building a static site with Eleventy, you often need the same navigation, author bio, or theme settings on dozens of pages. Copy‑and‑paste or hard‑coding these values in every template is error‑prone and hard to maintain. The question is: how can we keep a single source of truth while still allowing page‑specific overrides?
Thesis: Eleventy's Data Cascade Provides Automatic Inheritance
Eleventy automatically loads data files (JSON, YAML, JavaScript, Nunjucks) from a page’s directory and all ancestor directories. The loader merges these objects into one data context that the template receives. This means you can place a site.json at the project root, a blog.json in src/blog, and a post.json in src/blog/2026/; each page will receive a single merged object that respects the cascade order.
How the Cascade Works
- Loading order: Eleventy walks from the page’s folder up to the root, loading every data file it finds.
- Key precedence: Keys defined in a child directory override those from a parent. If a key exists in both, the child wins.
- Object merging: When the same key maps to an object in both the child and parent, Eleventy performs a shallow merge—child properties override parent properties, but missing keys are inherited.
- Functions: JavaScript data files can export functions. These run at build time and can compute dynamic values (e.g.,
new Date().toISOString()). The result is baked into the page’s context.
Because all data is evaluated during the Eleventy build, the values are static until the next build. This is suitable for site‑wide settings but not for per‑request dynamic data.
Concrete Example: A Blog Post Inheriting Navigation & Author Info
Assume the following project structure:
src/
├─ _data/
│ └─ site.json
├─ blog/
│ ├─ _data/
│ │ └─ blog.json
│ ├─ 2026/
│ │ ├─ _data/
│ │ │ └─ post.json
│ │ └─ my-first-post.md
Contents of site.json:
{
"title": "My Static Site",
"navigation": [
{"label": "Home", "url": "/"},
{"label": "Blog", "url": "/blog/"}
],
"author": {"name": "Jane Doe", "bio": "Web dev & writer."}
}
Contents of blog.json:
{
"navigation": [
{"label": "Home", "url": "/"},
{"label": "Blog", "url": "/blog/"},
{"label": "Archive", "url": "/blog/archive/"}
]
}
Contents of post.json:
{
"author": {"name": "John Smith", "bio": "Tech enthusiast."},
"publishDate": () => new Date().toISOString()
}
Now, in my-first-post.md we can reference these values directly:
---
layout: "post.njk"
---
{{ title }}
By {{ author.name }} on {{ publishDate | date: "MMMM d, yyyy" }}
{% for item in navigation %}
- {{ item.label }}
{% endfor %}
When Eleventy builds the page:
- It loads
site.jsonfirst. - Then it loads
blog.json, overriding thenavigationarray. - Finally, it loads
post.json, overridingauthorand addingpublishDate.
The resulting context for the template is a single object where:
authorcomes frompost.json.navigationcomes fromblog.json.- All other keys (e.g.,
title) come fromsite.json.
How to Verify the Cascade
- Build with debug:
npx eleventy --debugEleventy prints a log of data files it loads for each page. Confirm that the log shows the three files in the correct order.
- Inspect the generated HTML:
cat _site/blog/2026/my-first-post.html | grep -A3 "navigation"Check that the navigation list includes the “Archive” item from
blog.jsonand that the author name is “John Smith”. - Test dynamic functions:
cat _site/blog/2026/my-first-post.html | grep "publishDate"Verify that
publishDateis rendered as an ISO string. Remember, it is static until the next build.
Trade‑offs & Limitations
- Build‑time evaluation: Any function in a data file runs once during the build. If you need per‑request data (e.g., visitor count), you must fetch it client‑side or use a serverless function.
- Build time overhead: A deep cascade with many large data files can slow the build. Use
eleventy --debugto profile and consider flattening the hierarchy if performance degrades. - File naming pitfalls: A data file named
index.jsonin a subfolder will override anyindex.jsonin a parent folder. Plan your naming to avoid accidental overrides. - Global variables in JS data: If a JavaScript data file references globals (e.g.,
process.env), you must expose them via Eleventy’s configglobalDataor ensure they exist at build time.
Actionable Next Steps
- Start by creating a
_data/site.jsonwith site‑wide settings. - For each major content section, add a
_data/section.jsonto override or extend those settings. - Use
eleventy --debugduring development to confirm the cascade order. - Measure build times if you anticipate a large data tree; consider flattening or caching expensive computations.
- Document the cascade hierarchy in your project README so new contributors understand where to place data files.
By leveraging Eleventy's data cascade, you can keep your templates clean, avoid duplication, and maintain a single source of truth for site‑wide configuration while still allowing granular overrides per page or section.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.