Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

プレビューモード

注意: この機能はドラフトモードに置き換えられています。

Pages のドキュメントデータフェッチングのドキュメントで、getStaticPropsgetStaticPaths を使用してビルド時にページを事前レンダリングする方法(静的生成)について説明しました。

静的生成は、ヘッドレスCMSからデータをフェッチするページに役立ちます。しかし、ヘッドレスCMS上で下書きを作成し、そのドラフトをすぐにページでプレビューしたい場合は理想的ではありません。ユーザーは、Next.jsにビルド時ではなくリクエスト時にページをレンダリングし、公開されたコンテンツではなくドラフトコンテンツをフェッチしてほしいと考えます。Next.jsに、この特定のケースでのみ静的生成をバイパスしてほしいのです。

Next.jsには、この問題を解決するプレビューモードという機能があります。以下にその使用方法を説明します。

ステップ1:プレビューAPIルートの作成とアクセス

Next.jsのAPIルートに慣れていない場合は、まずAPIルートのドキュメントを確認してください。

まず、プレビューAPIルートを作成します。名前は任意です。例えば、pages/api/preview.js(TypeScriptを使用する場合は.ts)です。

このAPIルートでは、レスポンスオブジェクトでsetPreviewDataを呼び出す必要があります。setPreviewDataの引数はオブジェクトである必要があり、これはgetStaticPropsで使用できます(詳細は後述)。今のところ、{}を使用します。

export default function handler(req, res) {
  // ...
  res.setPreviewData({})
  // ...
}

res.setPreviewDataは、ブラウザにCookieを設定し、プレビューモードをオンにします。これらのCookieを含むNext.jsへのリクエストは、プレビューモードと見なされ、静的に生成されたページの動作が変更されます(詳細は後述)。

以下のようなAPIルートを作成し、ブラウザから手動でアクセスすることで、手動でテストできます:

pages/api/preview.js
// ブラウザから手動でテストするための簡単な例。
export default function handler(req, res) {
  res.setPreviewData({})
  res.end('プレビューモードが有効になりました')
}

ブラウザの開発者ツールで /api/preview にアクセスすると、このリクエストで __prerender_bypass__next_preview_data のCookieが設定されることに気づくでしょう。

ヘッドレスCMSから安全にアクセスする

実際には、ヘッドレスCMSから_安全に_このAPIルートを呼び出したいと思うでしょう。具体的な手順は使用しているヘッドレスCMSによって異なりますが、以下は一般的な手順です。

これらの手順は、使用しているヘッドレスCMSがカスタムプレビューURLの設定をサポートしていることを前提としています。サポートしていない場合でも、このメソッドを使用してプレビューURLを保護できますが、プレビューURLを手動で構築およびアクセスする必要があります。

まず、任意のトークンジェネレーターを使用して、シークレットトークン文字列を作成します。このシークレットは、Next.jsアプリとヘッドレスCMSのみが知っているものです。このシークレットにより、CMSにアクセスできない人がプレビューURLにアクセスするのを防ぎます。

次に、ヘッドレスCMSがカスタムプレビューURLの設定をサポートしている場合、以下をプレビューURLとして指定します。これは、プレビューAPIルートが pages/api/preview.js に配置されていることを前提としています。

Terminal
https://<your-site>/api/preview?secret=<token>&slug=<path>
  • <your-site> はデプロイメントドメインに置き換えます。
  • <token> は生成したシークレットトークンに置き換えます。
  • <path> はプレビューするページのパスです。/posts/foo をプレビューする場合、&slug=/posts/foo を使用します。

ヘッドレスCMSによっては、プレビューURLに変数を含めることができ、<path> をCMSのデータに基づいて動的に設定できます:&slug=/posts/{entry.fields.slug}

最後に、プレビューAPIルートで以下を行います:

  • シークレットが一致し、slugパラメータが存在することを確認します(存在しない場合、リクエストは失敗する必要があります)。
  • res.setPreviewDataを呼び出します。
  • ブラウザをslugで指定されたパスにリダイレクトします。(次の例では307リダイレクトを使用しています)。
export default async (req, res) => {
  // シークレットとnextパラメータを確認
  // このシークレットは、このAPIルートとCMSのみが知っているべきです
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: '無効なトークン' })
  }
 
  // ヘッドレスCMSをフェッチし、提供された`slug`が存在するか確認
  // getPostBySlugは、ヘッドレスCMSへの必要なフェッチロジックを実装します
  const post = await getPostBySlug(req.query.slug)
 
  // slugが存在しない場合、プレビューモードの有効化を防ぐ
  if (!post) {
    return res.status(401).json({ message: '無効なslug' })
  }
 
  // プレビューモードをCookieの設定によって有効化
  res.setPreviewData({})
 
  // フェッチしたポストのパスにリダイレクト
  // オープンリダイレクトの脆弱性を避けるため、req.query.slugにリダイレクトしません
  res.redirect(post.slug)
}

成功した場合、ブラウザはプレビューモードのCookieが設定された、プレビューしたいパスにリダイレクトされます。

ステップ2:getStaticPropsの更新

次のステップは、プレビューモードをサポートするためにgetStaticPropsを更新することです。

プレビューモードのCookie(res.setPreviewData経由)が設定された状態でページをリクエストすると、getStaticPropsは(ビルド時ではなく)リクエスト時に呼び出されます。

さらに、以下のcontextオブジェクトと共に呼び出されます:

  • context.previewtrue になります。
  • context.previewData は、setPreviewDataに使用された引数と同じになります。
export async function getStaticProps(context) {
  // プレビューモードのCookieが設定された状態でこのページをリクエストする場合:
  //
  // - context.preview は true になります
  // - context.previewData は
  //   `setPreviewData` に使用された引数と同じになります。
}

プレビューAPIルートで res.setPreviewData({}) を使用したため、context.previewData{} になります。必要に応じて、プレビューAPIルートから getStaticProps にセッション情報を渡すために使用できます。

getStaticPaths も使用している場合、context.params も利用可能になります。

プレビューデータのフェッチ

context.preview および/または context.previewData に基づいて異なるデータをフェッチするように getStaticProps を更新できます。

例えば、ヘッドレスCMSには下書き投稿用の異なるAPIエンドポイントがあるかもしれません。その場合、context.preview を使用してAPIエンドポイントのURLを以下のように変更できます:

export async function getStaticProps(context) {
  // context.preview が true の場合、APIエンドポイントに "/preview" を追加し、
  // 公開されたデータではなく下書きデータをリクエストします。
  // これは使用しているヘッドレスCMSによって異なります。
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

これで完了です!ヘッドレスCMSから、または手動でプレビューAPIルート(secretslug 付き)にアクセスすると、プレビューコンテンツを表示できるはずです。下書きを更新して公開しない場合、下書きをプレビューできるはずです。

ヘッドレスCMSでプレビューURLを設定するか、手動でアクセスし、プレビューを表示してください。

Terminal
https://<your-site>/api/preview?secret=<token>&slug=<path>

さらに詳しく

補足: レンダリング中、next/routerisPreview フラグを公開します。詳細はルーターオブジェクトのドキュメントを参照してください。

プレビューモードの期間を指定

setPreviewData は、オプションのセカンドパラメータとしてオプションオブジェクトを受け取ります。以下のキーを受け入れます:

  • maxAge: プレビューセッションの継続時間(秒単位)を指定します。
  • path: Cookieを適用するパスを指定します。デフォルトは / で、すべてのパスでプレビューモードを有効にします。
setPreviewData(data, {
  maxAge: 60 * 60, // プレビューモードのCookieは1時間で期限切れ
  path: '/about', // プレビューモードのCookieは /about のパスに適用されます
})

### プレビューモードのクッキーをクリア

デフォルトでは、プレビューモードのクッキーには有効期限が設定されていないため、ブラウザを閉じるとプレビューセッションが終了します。

プレビューモードのクッキーを手動でクリアするには、`clearPreviewData()`を呼び出すAPIルートを作成します:

```js filename="pages/api/clear-preview-mode-cookies.js"
export default function handler(req, res) {
  res.clearPreviewData({})
}

次に、/api/clear-preview-mode-cookiesにリクエストを送信してAPIルートを呼び出します。next/linkを使用してこのルートを呼び出す場合、リンクのプリフェッチ中にclearPreviewDataが呼び出されないように、prefetch={false}を渡す必要があります。

setPreviewDataの呼び出し時にパスが指定されている場合、同じパスをclearPreviewDataに渡す必要があります:

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  const { path } = req.query
 
  res.clearPreviewData({ path })
}

previewDataのサイズ制限

setPreviewDataにオブジェクトを渡し、getStaticPropsで利用可能にすることができます。ただし、データはクッキーに保存されるため、サイズ制限があります。現在、プレビューデータは2KBに制限されています。

getServerSidePropsで動作

プレビューモードはgetServerSidePropsでも動作します。previewpreviewDataを含むコンテキストオブジェクトでも利用可能です。

補足: プレビューモードを使用する際は、バイパスできないためCache-Controlヘッダーを設定しないでください。代わりに、ISRの使用をお勧めします。

APIルートで動作

APIルートは、リクエストオブジェクト下のpreviewpreviewDataにアクセスできます。例:

export default function myApiRoute(req, res) {
  const isPreview = req.preview
  const previewData = req.previewData
  // ...
}

next buildごとにユニーク

バイパスクッキーの値とpreviewDataを暗号化するプライベートキーは、next buildが完了するたびに変更されます。 これにより、バイパスクッキーを推測できないようにします。

補足: HTTPでプレビューモードをローカルでテストするには、ブラウザでサードパーティのクッキーとローカルストレージへのアクセスを許可する必要があります。