このページで分かること

  • select フィールドにレイアウトの選択を保存し、ページのルートでAstroのレイアウトコンポーネントに対応づける仕組み
  • pages コレクションへの template フィールドの追加方法(管理画面、またはシードデータ)
  • レイアウトコンポーネントの作り方と、レイアウトを増やすときの手順
難易度
実践
読む時間
2分
このページの目次

編集者がページごとにレイアウトを選べるようにします。たとえば、Default、Full Width、Landing Pageのようなレイアウトです。選択は select フィールドに保存し、ページのルートがその値をAstroのレイアウトコンポーネントに対応づけます。

仕組み

  1. pagesコレクションに、template という選択(select)フィールドを追加します
  2. 選択肢ごとにレイアウトコンポーネントを作成します
  3. ページのルートで、フィールドの値をレイアウトに対応づけます

この方法は、EmDashの選択フィールドとAstroのコンポーネントモデルを使います。

本サイトの補足 やさしい解説

WordPressの「ページテンプレート」(固定ページの編集画面でテンプレートを選ぶ機能)と同じことを、EmDashでは選択フィールドとAstroのコンポーネントで実現します。編集者は編集画面の選択肢から「Full Width」などを選び、その値がエントリーに保存されます。ページのルート(URLに対応するAstroのファイル)は、保存された値を見て、使うレイアウトコンポーネントを切り替えます。

フィールドの追加

管理画面で、pagesコレクションに、スラッグtemplate の選択フィールドを追加し、レイアウトの選択肢(たとえば「Default」と「Full Width」)を設定します。同じフィールドをシードデータで定義するには、次のオブジェクトをコレクションの fields 配列に追加します。

.emdash/seed.json
{
  "slug": "template",
  "label": "Template",
  "type": "select",
  "validation": {
    "options": ["Default", "Full Width"]
  },
  "defaultValue": "Default"
}

レイアウトコンポーネントの作成

各レイアウトは、コンテンツをベースレイアウトで包み、それぞれ異なるスタイルを当てます。

src/layouts/PageDefault.astro
---
import type { ContentEntry, InferCollectionData } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "./Base.astro";

interface Props {
  page: ContentEntry<InferCollectionData<"pages">>;
}

const { page } = Astro.props;
---

<Base title={page.data.title}>
  <article class="page-default">
    <h1>{page.data.title}</h1>
    <PortableText value={page.data.content} />
  </article>
</Base>

<style>
  .page-default {
    max-width: var(--content-width);
    margin: 0 auto;
    padding: 2rem 1rem;
  }
</style>
src/layouts/PageFullWidth.astro
---
import type { ContentEntry, InferCollectionData } from "emdash";
import { PortableText } from "emdash/ui";
import Base from "./Base.astro";

interface Props {
  page: ContentEntry<InferCollectionData<"pages">>;
}

const { page } = Astro.props;
---

<Base title={page.data.title}>
  <article class="page-wide">
    <h1>{page.data.title}</h1>
    <PortableText value={page.data.content} />
  </article>
</Base>

<style>
  .page-wide {
    max-width: var(--wide-width);
    margin: 0 auto;
    padding: 2rem 1rem;
  }
</style>

ルートへの接続

ページのルートで、各レイアウトをインポートし、templateの値を対応づけます。

src/pages/pages/[slug].astro
---
import { decodeSlug, getEmDashEntry } from "emdash";
import PageDefault from "../../layouts/PageDefault.astro";
import PageFullWidth from "../../layouts/PageFullWidth.astro";

const slug = decodeSlug(Astro.params.slug);

if (!slug) {
  return Astro.redirect("/404");
}

const { entry: page } = await getEmDashEntry("pages", slug);

if (!page) {
  return Astro.redirect("/404");
}

const layouts = new Map([
  ["Default", PageDefault],
  ["Full Width", PageFullWidth],
]);

const Layout = layouts.get(page.data.template ?? "") ?? PageDefault;
---

<Layout page={page} />

ルートは小さいままです。マークアップとスタイルは、それぞれのレイアウトコンポーネントが持ちます。レイアウトを追加する手順は、コンポーネントを作成し、選択フィールドに選択肢を追加し、対応表(Map)に1行追加するだけです。

本サイトの補足 やさしい解説

ルートのコードにある new Map([...]) が、「フィールドの値」と「使うレイアウト」の対応表です。値が空の場合や、対応表にない値の場合は、?? PageDefault によって既定のレイアウトが使われます。選択肢の文字列(例:"Full Width")は、シードファイル、管理画面の選択肢、この対応表のすべてで完全に一致させる必要があります。

レイアウトの追加

よく使われるレイアウトの選択肢は次のとおりです。

  • Default:幅の狭いコンテンツの列。読みやすさに向いています
  • Full Width:幅の広いコンテンツの領域。サイドバーはありません
  • Landing Page:ヘッダーとフッターがなく、ヒーローセクションがあります
  • Sidebar:コンテンツの横にウィジェットエリアがあります

どれも、src/layouts/ ディレクトリに置くAstroコンポーネントを1つ増やし、ルートのレイアウトの対応表に1項目を追加するだけです。