# Next.jsでヘッドレスCMSを使う：記事一覧・詳細・下書きプレビューの作り方

公開日: 2026-09-17 | 更新日: 2026-10-18 | URL: https://blog.genko.me/headless-cms-nextjs

> Next.jsのApp Routerにgenko.meを接続し、記事一覧・詳細ページ・保存済み下書きのプレビューを実装します。APIキーの設定から本番公開まで、コード例と確認手順をまとめました。

Next.jsで作ったサイトに、ブログやお知らせを追加したい。原稿を更新するたびにソースコードを編集する運用から、ブラウザで書いて公開する運用へ切り替えたい。そんなときに使えるのが、原稿の管理とサイトの表示を分けるヘッドレスCMSです。

この記事では、genko.meで記事を管理し、Next.jsのApp Routerで一覧と詳細を表示します。最後に、公開前の原稿を同じ詳細ページで確認できるようにします。サイトの見た目はNext.js側に残るので、既存のデザインに合わせて組み込めます。

2026年9月17日時点の公開版（CLI 0.2.3、SDK 0.3.0、React部品 0.3.1）と[公式クイックスタート](https://docs.genko.me/getting-started)に基づく手順です。

## 始める前に用意するもの

- 起動できるNext.js 16以降・React 19のApp Routerプロジェクト
- Node.js 20.12以降で、利用するNext.jsにも対応したバージョン
- genko.meのアカウントと、オーナー権限のあるワークスペース

以下はTypeScriptとNode.jsランタイムを使う例です。ファイルの置き場所は`app/`構成で示します。`src/app/`構成なら、ページは`src/app/`、CLIの生成ファイルは`src/lib/`になります。`@/`の参照先もプロジェクトに合わせてください。

## 1. 記事を入れるAPIを作る

genko.meのワークスペースでAPIを作成し、API名を「ブログ」、エンドポイントを`blog`、形式を「リスト形式」にします。リスト形式は、記事のように複数のコンテンツを扱うための形式です。

スキーマには、次の2項目を用意します。後のコードでフィールドIDを使うため、表示名だけでなくIDも合わせてください。

| 表示名 | フィールドID | 種類 | 必須 |
| --- | --- | --- | --- |
| タイトル | title | テキスト | はい |
| 本文 | body | リッチテキスト | はい |

続いて記事を1件作ります。タイトルと本文を入力し、エディターの「設定」で公開状態を「公開」にして保存してください。通常の配信APIは下書きを返さないので、接続確認には公開済みの記事を使います。

画面の操作は[APIとスキーマ](https://docs.genko.me/studio/apis)、[コンテンツの作成と公開](https://docs.genko.me/studio/content)でも確認できます。

## 2. APIキーを発行して、CLIで接続する

ワークスペースのメニューから「設定・プラン」→「APIキー」を開きます。このサイト専用のキーを作り、読み取り範囲を「選択したAPIのみ」にして`blog`を許可します。有効期間も設定し、作成直後に表示されるキーを控えてください。キー全文は後から再表示できません。

接続に使うワークスペースIDは、管理画面のURLが`https://genko.me/w/your-workspace`なら`your-workspace`です。

Next.jsプロジェクトの`package.json`があるディレクトリで、次を実行します。

```bash
npx @genko-me/cli init
```

ワークスペースID、APIキーを入力し、同期するAPIとして`blog`を選びます。CLIがSDKなどの依存パッケージを追加し、スキーマの型と接続用のクライアントを生成します。

- `lib/schema.ts`：APIとフィールドの型
- `lib/client.ts`：genko.meに接続するクライアント
- `genko.config.json`：次回の同期に使う設定
- `.env.local`：APIキーの保存先（既存の環境変数などにキーがある場合を除く）

APIキーはサーバー側の`GENKO_API_KEY`として扱います。`NEXT_PUBLIC_`を付けたり、ブラウザへ渡したりしないでください。`.env.local`がGitの追跡対象になっていないことも確認します。詳しくは[APIキーの管理](https://docs.genko.me/studio/api-keys)を参照してください。

CLIはページ自体を生成しません。次の2つのファイルを自分のプロジェクトに追加します。

## 3. 記事一覧を表示する

`app/blog/page.tsx`を作成します。このページはサーバーコンポーネントとして使うので、先頭に`"use client"`は付けません。

```tsx
import { client } from "@/lib/client";
import { List, ListItem, Pagination, Error, Null } from "@genko-me/react/next";

export default function BlogPage() {
  return (
    <main>
      <h1>ブログ</h1>
      <List api={client.blog} limit={6} orders="-publishedAt" href="/blog/{id}">
        <article>
          <h2><ListItem id="title" /></h2>
        </article>
        <Pagination />
        <Error template />
        <Null>公開されている記事はありません。</Null>
      </List>
    </main>
  );
}
```

`List`の中の`article`が記事ごとに繰り返され、`ListItem`がタイトルを表示します。`limit={6}`は1ページの件数、`orders="-publishedAt"`は公開日時の新しい順、`href`は詳細ページの行き先です。`Pagination`はページ送りを担当します。

開発サーバーを起動し、`/blog`を開いてください。先ほど公開した記事のタイトルが出れば、接続できています。環境変数を追加する前からサーバーが動いていた場合は、再起動します。

## 4. 記事の詳細ページを作る

次に`app/blog/[id]/page.tsx`を作成します。一覧のリンクから受け取った記事IDを`View`へ渡し、タイトルと本文を表示します。

```tsx
import { client } from "@/lib/client";
import { View, ViewItem, Error, Null } from "@genko-me/react/next";

type Props = {
  params: Promise<{ id: string }>;
  searchParams: Promise<Record<string, string | string[] | undefined>>;
};

export default async function BlogDetail({ params, searchParams }: Props) {
  const { id } = await params;

  return (
    <main>
      <View api={client.blog} id={id} searchParams={searchParams}>
        <h1><ViewItem id="title" /></h1>
        <ViewItem id="body" />
        <Error template />
        <Null>記事が見つかりません。</Null>
      </View>
    </main>
  );
}
```

一覧から記事を開き、タイトルと本文を確認します。見出しや余白、本文の装飾はサイト側のCSSで整えてください。各部品の指定方法は[List・Viewのリファレンス](https://docs.genko.me/sdk/components)にまとめています。

## 5. 公開前の原稿をプレビューする

管理画面のAPI設定で、プレビューURLを次の形に設定します。ドメインとパスは自分のサイトに置き換えてください。

```text
https://your-site.example/blog/{id}?draftKey={key}
```

開発中なら、自分の環境でアクセスできるローカルURLを設定できます。記事を下書きとして保存し、エディターの「プレビュー」を開くと、同じ詳細ページで保存済みの原稿を確認できます。

詳細ページで`searchParams`を`View`へ渡したのは、この`draftKey`を受け取るためです。この構成では、プレビュー専用のRoute Handlerを別に用意する必要はありません。

確認できるのは保存済みの内容です。未保存の編集は反映されません。また、下書きキーを含むURLは公開リンクとして共有しないでください。設定の詳細は[下書きプレビュー](https://docs.genko.me/guides/preview)を参照してください。

## 6. 本番環境に接続する

ホスティング先にも環境変数`GENKO_API_KEY`を設定してデプロイします。ローカルの`.env.local`は、本番環境へ自動で引き継がれません。デプロイ後は本番URLで一覧・詳細・下書きプレビューをそれぞれ確認します。

この例は記事を表示するための基本構成です。公開サイトとして仕上げる際は、記事ごとのタイトル・説明・OG画像などのメタデータと、存在しない記事の404処理もサイト側で整えてください。`Null`に文言を書くだけでは、HTTPの404設定まで完了したことにはなりません。

スキーマに項目を追加・変更したら、次のコマンドで型を同期します。

```bash
npx @genko-me/cli pull
```

## 表示されないときの確認先

| 症状 | 確認すること |
| --- | --- |
| 一覧が空 | 記事が「公開」で保存されているか。下書きは通常の一覧には出ません。 |
| 401エラー | キーの有効期間と、実行環境のGENKO\_API\_KEYを確認します。 |
| 404エラー | ワークスペースID、エンドポイント、記事IDに加え、キーがblogの読み取りを許可しているか確認します。 |
| client.blogが見つからない | CLIでblogを同期したか、生成先とimport先が一致するか確認します。 |
| 下書きがプレビューに出ない | 保存済みか、URLにdraftKeyが付くか、ViewへsearchParamsを渡しているか確認します。 |

まず1件の記事で、公開・表示・下書き確認の流れを通してみてください。動作を確認してから画像やカテゴリを加えると、接続の問題と見た目の調整を切り分けやすくなります。最初から画面のある構成を試したい方には、[お知らせページのテンプレート](https://docs.genko.me/guides/announcements-starter)も用意しています。
