本文へスキップ
Website Boilerplate

Data Access

本ページは、テンプレートや partial から Eleventy のデータ (グローバルデータ、ページデータ、Eleventy 組み込みの page オブジェクト) にアクセスする書き方を扱います。プリセットは eleventy シングルトンを提供しており、レイアウトから partial まで props を渡し続ける必要をなくします。

eleventy シングルトン

シングルトンは @tuqulore-inc/eleventy-preset/eleventy から import します。SSR 中に評価される JSX / MDX の中でだけ有効です。

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

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

partial から使うときも、レイアウト側から props を渡す必要はありません。partial は自分で eleventy を import して直接読みます。

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

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

参照できるプロパティ

プロパティ内容
eleventy.content子テンプレートを描画した HTML

eleventy.title, eleventy.description, etc.

frontmatter に書いた各値

eleventy.site, eleventy.nav, etc.

グローバルデータ (_data/ 配下) のトップレベルキー

eleventy.page

Eleventy の

page 変数

(URL、日付など)

eleventy.locale

frontmatter の locale フィールドに書いた値

eleventy.collections

Eleventy の collection。要素の frontmatter は data の下にあります

eleventy.content は親レイアウトが子の描画結果を受け取るための箱です。中身は既に HTML 文字列なので、dangerouslySetInnerHTML で埋め込みます。JSX ツリーに直接埋めると再描画のコストを二重に払う形になります。

グローバルデータの書き方

src/_data/ に置いたファイルが、そのままシングルトンのトップレベルキーになります。

// src/_data/site.js
export default {
  name: "本ボイラープレートのドキュメント",
  description: "設計と使い方",
  url: process.env.SITE_URL,
  author: "tuqulore inc.",
};
// テンプレートから
<title>{eleventy.site.name}</title>

サブディレクトリを切ると、ネストしたキーになります。

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>);
}

現在のページの locale に応じてどちらを引くかは、テンプレート側で eleventy.nav[eleventy.locale] の形で分岐します。本ドキュメントの sidebar 実装がその例です。

データに型を付ける

本プリセットは、eleventy シングルトンの型定義を提供しています。 page などの Eleventy 組み込みのデータと、title や description のようなよく使う frontmatter には、設定なしで型が付きます。

グローバルデータや独自の frontmatter の型は、プロジェクトごとに TypeScript の宣言マージ(declaration merging)で追加します。 型定義ファイルを置き、EleventyUserData インターフェースにデータの形を宣言します。

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;
  }
}

これで eleventy.site.name は string に解決され、eleventy.site.nmae のような打ち間違いは型エラーになります。 site と nav の型はデータファイルから導いているので、データファイルを書き換えると型も追随します。

宣言マージの対象には、テンプレートが eleventy を import しているモジュール(プリセット経由なら @tuqulore-inc/eleventy-preset/eleventy)を指定します。 プリセットが内部で使うプラグインのモジュール名を指定するわけにはいきません。pnpm で依存を入れたプロジェクトからはそのモジュールを解決できず、declare module は型の追加ではなく新しいモジュールの宣言として扱われ、eleventy の型が失われるためです。 同じ理由で、型定義ファイルには少なくとも一つのトップレベルの import を置きます。import も export もないファイルはモジュールとして扱われません。

default テンプレートには、この型定義ファイルが最初から含まれています。

collection の要素の型は、宣言マージを手で書かずにスキーマから導けます。 collection のディレクトリに _schema.ts を置くと、本プリセットに同梱した @tuqulore-inc/eleventy-collections が、そのスキーマからビルド時の検証と eleventy.collections.<name> の型の両方を導きます。 _schema.ts というファイル名はこの用途に予約されています。 詳しくは eleventy-collections を参照してください。

SSR 中のみ有効

eleventy シングルトンは SSR 中にだけ有効です。クライアント側で hydrate される Island からは参照できません。SSR 側で解決した値を Island に渡したいときは、<Island> の props として明示的に渡します。

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

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

<Island> に渡した props は、SSR 側の描画とクライアント側の hydrate の両方に流れます。SSR で描いたマークアップとクライアントで hydrate した後の DOM が一致することが保証されます。

Island 内で SSR データを読みたい場合

Island の中で eleventy を import して使うと、クライアント側では import 自体がエラーになります。SSR に依存する値は、常に <Island> の props 経由で受け渡すのが規約です。

Island 内で完結する状態 (ユーザー入力、フェッチ結果) はコンポーネント内部の useState / useEffect で持ち、SSR 由来の初期値は props として受け取ります。