Astro.js ブログ記事にアイキャッチ画像を追加する

Astroこコンテンツスキーマをカスタマイズすることでフロントマターでアイキャッチ画像を追加することができます。ここでは、AstroのContent Collectionsと `<Image>` コンポーネントを使って、記事にアイキャッチ画像を追加・表示する手順を紹介します。

**前提:**

* Content Collections APIを使ってブログ記事 (`blog` コレクション) を管理している
* `src/content/config.ts` でスキーマを定義している

## アイキャッチ画像 (featureImage) の追加手順

### 1.画像の準備と配置

まず、記事に使用したい画像ファイルを用意します。

用意した画像をプロジェクト内の **`src/assets/`** ディレクトリ以下（例: `src/assets/images/`）に配置します。ここに置くことで、Astroが画像を最適化してくれます。

```
your-project/
└── src/
    ├── assets/
    │   └── images/      <-- ここに画像を配置 (例: post1.jpg, post2.png)
    ├── content/
    │   └── blog/
    ├── pages/
    └── layouts/
```

### 2.スキーマ定義の更新 (`src/content/config.ts`)

`src/content/config.ts` ファイルを開き、`blog` コレクションの `schema` を編集して、アイキャッチ画像用のフィールドを追加します。Astroの `image()` ヘルパー関数を使います。

```typescript
// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blogCollection = defineCollection({
  type: 'content',
  // スキーマ定義を ({ image }) => z.object({...}) の形に変更する必要があるかも
  schema: ({ image }) => z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    description: z.string().optional(),
    tags: z.array(z.string()).optional(),
    url: z.string().optional(),
    // ↓ アイキャッチ画像用のフィールドを追加
    featureImage: image().optional(), // 画像はオプショナルにする場合
    featureImageAlt: z.string().optional(), // 代替テキストもオプショナルに
    // ↑ ここまで追加
  }),
});

export const collections = {
  'blog': blogCollection,
};
```
* `schema:` の後の定義を `({ image }) => z.object({...})` という形式に変更します。`image` は `image()` ヘルパー関数を使うために必要です
* `featureImage: image()`: 画像フィールドを定義します。`.refine()` で画像の最小幅などを検証したり、`.optional()` で画像がない記事も許容するように設定できます
* `featureImageAlt: z.string()`: 画像の代替テキスト（alt属性）用のフィールドです。画像に合わせて `.optional()` にしておくと良いでしょう

### 3.Markdown フロントマターの更新

アイキャッチ画像を設定したい記事のMarkdownファイル (`src/content/blog/` 内) を開き、フロントマターに `featureImage` と `featureImageAlt` を追記します。

```markdown
---
title: '私の最初の投稿'
pubDate: 2025-04-14
tags: ["Astro", "Blog"]
# ↓ アイキャッチ画像の情報を追加
featureImage: '../../assets/images/post1.jpg' # Markdownファイルから画像ファイルへの相対パス
featureImageAlt: 'ノートパソコンとコーヒーカップが置かれた机'
# ↑ ここまで追加
---

記事の本文...
```
* `featureImage:` には、**このMarkdownファイルから見た画像ファイルへの相対パス** を指定します。例のように `src/assets/images/` に置いた場合は、`../../assets/images/画像ファイル名` のようになります。
* `featureImageAlt:` には、画像の内容を具体的に説明する代替テキストを記述します。これはアクセシビリティのために非常に重要です。

### 4.テンプレートでの画像表示 (`.astro` ファイル)

アイキャッチ画像を表示したいテンプレートファイル（例: 個別記事ページの `src/pages/[...slug].astro` や、一覧ページの `src/pages/blog/[...page].astro`）を開き、`<Image>` コンポーネントを使って画像を表示します。

個別記事ページ (`src/pages/[...slug].astro`) での表示例:

```astro
---
// src/pages/[...slug].astro
import { getCollection, getEntry } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import { Image } from 'astro:assets'; // ← Imageコンポーネントをインポート

// --- getStaticPaths と props の受け取り (変更なし) ---
const { post } = Astro.props;
const { Content } = await post.render();
---
<BaseLayout title={post.data.title}>
  <article>
    <h1>{post.data.title}</h1>

    {/* ↓ アイキャッチ画像を表示 */}
    {post.data.featureImage && post.data.featureImageAlt && (
      <Image
        src={post.data.featureImage}
        alt={post.data.featureImageAlt}
        width={720}  {/* 表示したい幅を指定 */}
        aspectRatio="16:9" {/* アスペクト比を指定すると高さは自動計算されることが多い */}
        format="webp" {/* 最適なフォーマットを指定 (任意) */}
        quality={80} {/* 画質を指定 (任意) */}
        loading="lazy" {/* 遅延読み込み (任意) */}
        decoding="async" {/* 非同期デコード (任意) */}
      />
    )}
    {/* ↑ ここまで */}

    <p>公開日: {post.data.pubDate.toLocaleDateString()}</p>
    {/* ... 他のコンテンツ ... */}
    <Content />
  </article>
</BaseLayout>
```
* **`import { Image } from 'astro:assets';`** を忘れずに追加します。
* `post.data.featureImage` と `post.data.featureImageAlt` が両方存在する場合にのみ `<Image>` を表示するように条件分岐 (`&&`) します。
* `<Image>` コンポーネントを使用します。
    * `src={post.data.featureImage}`: スキーマで処理された画像データを渡します。
    * `alt={post.data.featureImageAlt}`: **必須** です。代替テキストを渡します。
    * `width`: 画像の表示幅を指定します。
    * `aspectRatio` または `height`: アスペクト比か高さを指定すると、レイアウトシフトを防げます。
    * `format`, `quality`, `loading`, `decoding` など、様々な最適化オプションを指定できます。

### 開発サーバーで確認

`content/config.ts` を変更したので、一度開発サーバーを **再起動** するのが確実です (`Ctrl+C` で停止し、再度 `npm run dev` など)。

ブラウザでアイキャッチ画像を設定した記事ページや一覧ページにアクセスし、画像が正しく表示されているか確認してください。画像の読み込みやレイアウト崩れがないかもチェックしましょう。