Skip to main content
Website Boilerplate

Data Access

This page covers how templates and partials read Eleventy data (global data, page data, and the built-in page object). The preset ships an eleventy singleton so you never have to prop-drill values from layouts down to partials.

The eleventy singleton

Import the singleton from @tuqulore-inc/eleventy-preset/eleventy. It is only valid inside JSX / MDX evaluated during SSR.

import { eleventy } from "@tuqulore-inc/eleventy-preset/eleventy";

<title>
  {eleventy.title} | {eleventy.site.name}
</title>;

Partials work the same way — they import eleventy themselves rather than relying on props from the surrounding layout.

import { eleventy } from "@tuqulore-inc/eleventy-preset/eleventy";

<footer>
  <p>&copy; {eleventy.site.author}</p>
</footer>;

What is on the singleton

PropertyDescription
eleventy.contentRendered HTML of the child template

eleventy.title, eleventy.description, etc.

Values from the current page's frontmatter

eleventy.site, eleventy.nav, etc.

Top-level keys of the global data under _data/

eleventy.page

Eleventy's

page variable

(URL, date, and so on)

eleventy.locale

The locale field from the page's frontmatter

eleventy.collections

Eleventy collections. Front matter of an entry sits under data

eleventy.content is where a parent layout receives its child's rendered HTML. The value is already an HTML string, so embed it via dangerouslySetInnerHTML. Injecting it into the JSX tree directly would re-parse the child's output on top of the parent render, paying the cost twice.

Writing global data

Files under src/_data/ become top-level keys on the singleton.

// src/_data/site.js
export default {
  name: "My Website",
  description: "About this site",
  url: process.env.SITE_URL,
  author: "tuqulore inc.",
};
// From a template
<title>{eleventy.site.name}</title>

Sub-directories become nested keys:

src/_data/nav/ja.json
src/_data/nav/en.json
{
  eleventy.nav.ja.map((item) => <a href={item.path}>{item.name}</a>);
}
{
  eleventy.nav.en.map((item) => <a href={item.path}>{item.name}</a>);
}

Templates typically pick which side to read based on the current page's locale: eleventy.nav[eleventy.locale]. This is exactly what this documentation's sidebar does.

Typing the data

The preset ships type definitions for the eleventy singleton. Eleventy's built-in data such as page, and common front matter fields such as title and description, are typed without any setup.

Types for global data and project-specific front matter are added per project through TypeScript declaration merging. Add a type definition file and declare the shape of your data on the EleventyUserData interface.

import type nav from "./src/_data/nav.json";
import type site from "./src/_data/site.js";

declare module "@tuqulore-inc/eleventy-preset/eleventy" {
  interface EleventyUserData {
    site: typeof site;
    nav: typeof nav;
    ogp?: string;
  }
}

With this, eleventy.site.name resolves to string, and a typo such as eleventy.site.nmae becomes a type error. The types of site and nav are derived from the data files, so they follow edits to those files.

Target the module your templates import eleventy from (@tuqulore-inc/eleventy-preset/eleventy when going through the preset). Do not target the plugin module the preset uses internally: a project installed with pnpm cannot resolve it, so declare module declares a new module instead of extending the types, and eleventy loses its type. For the same reason, keep at least one top-level import in the type definition file. A file with neither import nor export is not treated as a module.

The default template ships this type definition file out of the box.

Collection entry types can be derived from schemas instead of hand-written declaration merging. Placing a _schema.ts in a collection directory lets @tuqulore-inc/eleventy-collections, which the preset bundles, derive both build-time validation and the type of eleventy.collections.<name> from that schema. The file name _schema.ts is reserved for this purpose. See eleventy-collections for details.

Only valid during SSR

The eleventy singleton is only valid during SSR. Islands hydrated on the client cannot see it. To pass SSR-side values into an Island, hand them in as props on <Island>.

import { Island } from "@tuqulore-inc/eleventy-preset/island";
import Nav from "./nav.client.jsx";

<Island component={Nav} on="interaction" items={eleventy.nav.ja} />;

Props passed to <Island> flow to both the SSR render and the client-side hydration. What the browser sees after hydration matches what SSR rendered.

Reading SSR-only data inside an Island

Importing eleventy inside a .client.jsx file fails on the client — the import itself does not resolve. The rule is: SSR-only values always come in through <Island> props.

State that lives entirely inside the Island (user input, fetch results) is held with the component's own useState / useEffect. Anything that originated on the server side arrives as a prop.