Stop Hardcoding Main Text! How to Structure and Flexibly Manage 'Summaries' and 'Summary Lists' with Next.js and ACF

Stop Hardcoding Main Text! How to Structure and Flexibly Manage 'Summaries' and 'Summary Lists' with Next.js and ACF のビジュアル

Are you struggling with managing 'summaries' or 'summary lists' when operating a blog with Next.js and Headless WordPress (ACF)?
This article explains how to separate and structure them from the main content using ACF custom fields (including repeaters).
We introduce specific steps and code examples for fetching data from Next.js via WPGraphQL and displaying it flexibly.

  • ACFによる課題解決標準の抜粋機能や本文への直書きの課題を、ACFの専用フィールドで解決します。
  • WordPress側の設定「サマリー」用のテキストエリアと、「要約リスト」用のリピーターフィールドの具体的な設定方法を解説します。
  • Next.jsでの取得方法WPGraphQL(またはREST API)を使い、定義したカスタムフィールドのデータをNext.js側で取得・表示するコード例を紹介します。
  • コンテンツ構造化の利点本文とメタ情報(要約など)を分離することで、CMSの運用性とフロントエンドの柔軟性が向上します。

Next.jsとヘッドレスWordPress運用における「サマリー」管理の課題

Next.jsによる高速なフロントエンドと、WordPressの柔軟な編集体験を組み合わせたヘッドレスCMS構成は、現代のWeb制作において非常に強力な選択肢です。しかし、この構成特有の「コンテンツ管理の悩み」に直面している方も少なくありません。

特に、「記事一覧ページに表示するサマリー(抜粋)」と「記事詳細ページの冒頭に置く要約リスト」の扱いは、多くの開発者が悩むポイントです。

課題1: 標準「抜粋」機能の限界と一覧ページでの表示問題

WordPressには標準で「抜粋(excerpt)」機能が備わっています。しかし、このフィールドはデフォルトでプレーンテキストしか受け付けず、HTMLタグが許可されていません。そのため、「サマリー内の一部を強調したい」「改行を入れたい」といったデザイン上の要求に応えにくいという問題があります。

かといって、抜粋欄が空の場合に適用される「本文の先頭から自動抜粋(`the_excerpt()`の挙動)」に頼ると、意図しない箇所で文章が途切れたり、本文冒頭の画像やコードブロックが中途半端に含まれてしまい、一覧ページのデザイン崩れを引き起こす原因となります。

課題2: 本文冒頭に「要約リスト」を含めることのデメリット

SEOやCRO(コンバージョン率最適化)の観点から、記事の冒頭に「この記事のポイント」や「要約リスト」を設置することは非常に有効です。読者は記事全体を読まずとも、概要を素早く把握できます。

しかし、この要約リストをWordPressの本文(`the_content` / Gutenbergエディタ)の先頭に直接書き込んでしまうと、前述の「本文の自動抜粋」機能が作動した際に、この要約リストがそのまま一覧ページのサマリーとして表示されてしまいます。

私たちが一覧ページで見せたいのは「記事全体を要約したサマリー」であり、「記事のポイントを箇条書きにしたリスト」ではありません。このように、本文(content)にメタ的な情報(サマリーや要約)が混在することは、コンテンツの再利用性を著しく低下させます。


解決策はACFカスタムフィールドによる「コンテンツの構造化」

これらの課題を根本的に解決するアプローチが、「コンテンツの構造化」です。

具体的には、WordPressの本文(content)エリアは純粋な「記事本文」だけを管理するものと割り切り、「サマリー」や「要約リスト」といったメタ的な情報は、専用の入力欄(カスタムフィールド)として分離・管理します。

なぜACF(Advanced Custom Fields)が最適なのか

この「カスタムフィールド」を直感的かつ柔軟に作成・管理できるのが、Advanced Custom Fields (ACF) プラグインです。

ACFを使えば、以下のような対応が容易になります。

  • サマリー用フィールド: HTMLタグの使用を許可できる「テキストエリア」フィールドを作成できます。
  • 要約リスト用フィールド: 「見出し」と「説明」のセットを動的に追加・削除・並べ替えできる「リピーター(繰り返し)フィールド」を作成できます。

このようにコンテンツを構造化することで、Next.js側(フロントエンド)は、必要なデータをAPI経由でピンポイントに取得し、デザイナーが意図した通りの場所に正確に配置できるようになります。


【手順1】WordPress (ACF) でカスタムフィールドを定義する

まず、WordPress側でACFプラグインをインストールし、有効化していることを前提とします(※リピーターフィールドはACF Pro版の機能ですが、必須級の投資です)。

「カスタムフィールド」メニューから新規フィールドグループを作成し、投稿タイプ「投稿」に関連付けます。

「サマリー(summary)」フィールドの作成(テキストエリア)

記事一覧ページで使いたい、HTMLを許容するサマリー用のフィールドを作成します。

フィールド ラベル:

サマリー

フィールド名:

summary

フィールドタイプ:

テキストエリア

改行:

自動的に <br> を追加する(または、WYSIWYGエディタを選択しても良いでしょう)

これで、投稿編集画面に「サマリー」という専用の入力欄が表示されます。

「要約リスト(point_list)」フィールドの作成(リピーター)

次に、記事詳細ページの冒頭に表示する「要約リスト」用のフィールドを作成します。これは、複数の「見出し」と「説明」のペアを管理できるリピーターフィールドとして定義します。

フィールド ラベル:

要約リスト

フィールド名:

point_list

フィールドタイプ:

リピーター

リピーターフィールドの内部構造(サブフィールド)

「要約リスト(point_list)」フィールドの中に、以下の2つのサブフィールドを作成します。

  1. ポイント見出し
    • フィールド ラベル: ポイント見出し
    • フィールド名: point_title
    • フィールドタイプ: テキスト
  2. ポイント説明
    • フィールド ラベル: ポイント説明
    • フィールド名: point_description
    • フィールドタイプ: テキストエリア(改行を許可)

これにより、編集者は「行を追加」ボタンを押すだけで、要約の項目を好きなだけ追加できるようになります。


【手順2】API (WPGraphQL / REST) へのフィールド登録

作成したACFフィールドをNext.jsから取得できるように、APIで公開(エクスポーズ)する必要があります。ヘッドレス構成では、WPGraphQLを使用する方法が最も推奨されます。

(推奨) WPGraphQL ACFアドオンを使ったGraphQLスキーマへの登録

WPGraphQL for Advanced Custom Fields というアドオン(無料プラグイン)をインストールして有効化します。

その後、先ほど作成したACFのフィールドグループ設定に戻り、「GraphQL で表示」という設定項目を「はい」に変更します。また、各フィールド(`summary`, point_list)の設定内にも「GraphQL で表示」のトグルがある場合は、それもオンにします。

WPGraphQL ACFアドオンでスキーマに登録

これだけで、WordPressのGraphQLスキーマ(GraphiQL IDEなどで確認可能)に、`summary` や pointList といったフィールドが自動的に追加されます。

(参考) REST API への登録方法

もしREST APIを使用している場合、ACF Pro版であればフィールドグループの設定で「REST API で表示」を「はい」にするだけで公開されます。無料版の場合は、`functions.php` などで register_rest_field を使った追加実装が必要になる場合があります。


【手順3】Next.js側でのデータ取得と表示(GraphQL例)

最後に、Next.js側(`getStaticProps` や getServerSideProps、またはApp Routerのfetch)で、WPGraphQLエンドポイントからデータを取得し、コンポーネントに渡して表示します。

記事一覧ページ(アーカイブ)での「サマリー」取得

一覧ページでは、各投稿のID、タイトル、スラッグなどに加えて、カスタムフィールドの summary を取得します。

クエリ例 (GraphQL)


query GetPostArchives {
  posts(first: 10) {
    nodes {
      id
      title
      slug
      # ACFで追加したフィールド(フィールドグループ名が 'postFields' の場合)
      postFields {
        summary
      }
    }
  }
}
    

※フィールドグループ名(例: postFields)は、ACFのフィールドグループ設定で指定した「GraphQL フィールド名」に応じて変わります。

実装コード例 (React / JSX)

取得したデータを ArchivePage コンポーネントなどで展開します。`summary` フィールドはHTMLを含む可能性があるため、`dangerouslySetInnerHTML` を使用してレンダリングします。


// pages/blog/index.js (Pages Router例)

export default function ArchivePage({ posts }) {
  return (
    <div>
      <h1>ブログ一覧</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <h2>{post.title}</h2>
            {/* ACFのサマリーフィールドを表示 */}
            {post.postFields?.summary && (
              <div
                className="post-summary"
                dangerouslySetInnerHTML={{ __html: post.postFields.summary }}
              />
            )}
            <a href={`/blog/${post.slug}`}>続きを読む</a>
          </li>
        ))}
      </ul>
    </div>
  );
}
    

セキュリティノート: dangerouslySetInnerHTML を使用する際は、入力ソースが信頼できること(CMS管理画面からの入力であること)を確認してください。ACFのテキストエリアはXSSのリスクを軽減するサニタイズ機能を持っていますが、万全を期す場合は dompurify などのライブラリを併用することも検討してください。

記事詳細ページでの「要約リスト」取得と表示

詳細ページでは、本文(`content`)に加えて、リピーターフィールドの pointList を取得します。`pointList` はオブジェクトの配列として返されます。

クエリ例 (GraphQL)


query GetPostDetails($slug: ID!) {
  post(id: $slug, idType: SLUG) {
    id
    title
    content # 本文
    # ACFフィールド
    postFields {
      # リピーターフィールド(サブフィールドも指定)
      pointList {
        pointTitle
        pointDescription
      }
    }
  }
}
    

実装コード例 (React / JSX)

pointList は配列なので、`map()` メソッドでループ処理してリスト(`dl` タグや ul タグなど)としてレンダリングします。


// pages/blog/[slug].js (Pages Router例)

// (getStaticProps/Paths ... 省略)

export default function PostPage({ post }) {
  const points = post.postFields?.pointList;

  return (
    <article>
      <h1>{post.title}</h1>

      {/* --- 要約リスト --- */}
      {points && points.length > 0 && (
        <div className="summary-box">
          <h2>この記事のポイント</h2>
          <dl>
            {points.map((item, index) => (
              <React.Fragment key={index}>
                <dt>{item.pointTitle}</dt>
                {/* 説明文(改行を <br> に変換する場合など) */}
                <dd dangerouslySetInnerHTML={{ __html: item.pointDescription.replace(/\n/g, '<br />') }} />
              </React.Fragment>
            ))}
          </dl>
        </div>
      )}
      {/* --- /要約リスト --- */}


      {/* --- 本文 --- */}
      <div
        className="post-content"
        dangerouslySetInnerHTML={{ __html: post.content }}
      />
      {/* --- /本文 --- */}

    </article>
  );
}
    

まとめ: ACFによる構造化でNext.jsとWordPressの連携を強化しよう

今回は、Next.jsとヘッドレスWordPressの構成において、ACF(Advanced Custom Fields)を活用して「サマリー」と「要約リスト」を管理・表示する方法を解説しました。

最大のポイントは、「本文(content)」と「メタ的な情報(summary, point_list)」を明確に分離することです。

  1. WordPress側で、ACFを使いコンテンツを構造化されたフィールドとして定義する。
  2. WPGraphQL(またはREST API)経由で、必要なデータをピンポイントで取得する。
  3. Next.js側で、取得したデータを意図したレイアウトに組み込む。

このアプローチにより、編集者は直感的にコンテンツを入稿でき、開発者はフロントエンドの柔軟性とパフォーマンスを最大限に高めることができます。ぜひ、貴社のヘッドレスCMS運用にお役立てください。


Next.jsでのヘッドレスCMS構築にお悩みですか?

「Next.jsとWordPressの連携がうまくいかない」
「パフォーマンスと編集体験を両立させたい」
「ACFのより高度な活用法を知りたい」

当社は、Next.jsとヘッドレスCMS(WordPress, microCMSなど)を用いた高パフォーマンスなWebサイト構築を得意としています。現状の課題ヒアリングから最適な構成のご提案まで、まずはお気軽にご相談ください。

Was this article helpful?