コンテンツへスキップ

プレビューモード

: この機能はDraft Modeによって置き換えられました。

Pages ドキュメント」と「Data Fetching ドキュメント」では、getStaticPropsgetStaticPaths を使用してビルド時にページをプリレンダリングする方法(静的生成)について説明しました。

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

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は、プレビューモードをオンにするためのクッキーをブラウザに設定します。これらのクッキーを含む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_dataというクッキーが設定されていることに気づくでしょう。

ヘッドレス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に変数を追加できる場合があります。これにより、CMSのデータに基づいて<path>を動的に設定できます(例: &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)
}

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

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

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

プレビューモードのクッキーが設定された(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フラグを公開します。詳細については「router オブジェクトのドキュメント」を参照してください。

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

setPreviewDataは、オプションの第2引数としてオプションオブジェクトを受け取ります。以下のキーを受け入れます。

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

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

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

プレビューモードのクッキーを手動でクリアするには、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で利用可能にすることができます。ただし、データはクッキーに保存されるため、サイズ制限があります。現在、プレビューデータは2KBに制限されています。

getServerSidePropsでも動作します

プレビューモードはgetServerSidePropsでも動作します。previewpreviewDataを含むcontextオブジェクトでも利用可能になります。

補足: プレビューモードを使用している場合、Cache-Controlヘッダーを設定すべきではありません。これはバイパスできないためです。代わりに、ISRの使用をお勧めします。

API Routesでも動作します

API Routesは、リクエストオブジェクトの下にあるpreviewpreviewDataにアクセスできます。例えば、

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

next buildごとにユニーク

next buildが完了すると、バイパスクッキーの値とpreviewDataを暗号化するための秘密鍵の両方が変更されます。これにより、バイパスクッキーが推測されないようにします。

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