コンテンツにスキップ
Pages Routerガイドプレビューモード

Next.jsのプレビューモードでコンテンツをプレビューする方法

これはレガシーAPIであり、推奨されなくなりました。後方互換性のために引き続きサポートされています。

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

PagesドキュメントとData Fetchingドキュメントでは、getStaticPropsgetStaticPathsを使用して、ページをビルド時に事前レンダリングする方法(Static Generation)について説明しました。

Static Generationは、ヘッドレスCMSからデータを取得する際に役立ちます。しかし、ヘッドレスCMSでドラフトを作成し、ページでそのドラフトをすぐにプレビューしたい場合には理想的ではありません。この場合、Next.jsにビルド時ではなくリクエスト時にこれらのページをレンダリングし、公開コンテンツではなくドラフトコンテンツを取得させたいはずです。Next.jsには、この特定の場合にのみStatic Generationをバイパスさせたいと考えるでしょう。

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

ステップ1: プレビューAPIルートを作成し、アクセスする

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

まず、**プレビュー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
// simple example for testing it manually from your browser.
export default function handler(req, res) {
  res.setPreviewData({})
  res.end('Preview mode enabled')
}

ブラウザの開発者ツールを開き、/api/previewにアクセスすると、このリクエストで__prerender_bypassおよび__next_preview_dataCookieが設定されていることに気づくでしょう。

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

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

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

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

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

ターミナル
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) => {
  // Check the secret and next parameters
  // This secret should only be known to this API route and the CMS
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }
 
  // Fetch the headless CMS to check if the provided `slug` exists
  // getPostBySlug would implement the required fetching logic to the headless CMS
  const post = await getPostBySlug(req.query.slug)
 
  // If the slug doesn't exist prevent preview mode from being enabled
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }
 
  // Enable Preview Mode by setting the cookies
  res.setPreviewData({})
 
  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
  res.redirect(post.slug)
}

成功すると、ブラウザはプレビューモードCookieが設定された状態で、希望するプレビューパスにリダイレクトされます。

ステップ2: getStaticPropsを更新する

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

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

さらに、以下のプロパティを持つcontextオブジェクトが渡されて呼び出されます。

  • context.previewtrueになります。
  • context.previewDataは、setPreviewDataに使用された引数と同じになります。
export async function getStaticProps(context) {
  // If you request this page with the preview mode cookies set:
  //
  // - context.preview will be true
  // - context.previewData will be the same as
  //   the argument used for `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) {
  // If context.preview is true, append "/preview" to the API endpoint
  // to request draft data instead of published data. This will vary
  // based on which headless CMS you're using.
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

これで完了です!ヘッドレスCMSから(secretslugを指定して)プレビューAPIルートにアクセスするか、手動でアクセスすれば、プレビューコンテンツが表示されるはずです。ドラフトを公開せずに更新した場合も、ドラフトをプレビューできるはずです。

これをヘッドレスCMSのプレビューURLとして設定するか、手動でアクセスすると、プレビューが表示されるはずです。

ターミナル
https://<your-site>/api/preview?secret=<token>&slug=<path>

詳細

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

プレビューモードの有効期間を指定する

setPreviewDataは、オプションの第二引数として、オプションオブジェクトを受け取ります。以下のキーが利用可能です。

  • maxAge: プレビューセッションが持続する時間(秒)を指定します。
  • path: Cookieが適用されるパスを指定します。デフォルトは/で、すべてのパスでプレビューモードが有効になります。
setPreviewData(data, {
  maxAge: 60 * 60, // The preview mode cookies expire in 1 hour
  path: '/about', // The preview mode cookies apply to paths with /about
})

プレビューモードのCookieをクリアする

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

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

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で利用できるようにすることができます。ただし、データはCookieに保存されるため、サイズ制限があります。現在、プレビューデータは2KBに制限されています。

getServerSidePropsと連携する

プレビューモードはgetServerSidePropsでも機能します。contextオブジェクトにはpreviewpreviewDataが含まれます。

知っておくと良いこと: プレビューモードを使用する場合、Cache-Controlヘッダーを設定しないでください。これはバイパスできないためです。代わりに、ISRを使用することをお勧めします。

APIルートと連携する

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

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

next buildごとに一意

バイパスCookieの値とpreviewDataを暗号化するための秘密鍵は、next buildが完了すると変更されます。これにより、バイパスCookieが推測されるのを防ぎます。

知っておくと良いこと: ローカルでHTTP経由でプレビューモードをテストするには、ブラウザがサードパーティCookieとローカルストレージへのアクセスを許可する必要があります。