コンテンツへスキップ

プレビューモード

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

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

静的生成は、ページがヘッドレスCMSからデータを取得する場合に便利です。しかし、ヘッドレスCMSで下書きを作成し、その下書きをページ上で即座に**プレビュー**したい場合には理想的ではありません。Next.jsには、ビルド時ではなく**リクエスト時**にこれらのページをレンダリングし、公開されたコンテンツの代わりに下書きコンテンツを取得してほしいでしょう。Next.jsに、この特定のケースでのみ静的生成をバイパスさせたいでしょう。

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

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

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

まず、プレビュー 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 のクッキーが設定されていることがわかります。

Headless CMS から安全にアクセスする

実際には、headless CMS からこの API ルートを安全に呼び出す必要があります。具体的な手順は、使用している headless CMS によって異なりますが、一般的な手順をいくつかご紹介します。

これらの手順は、使用している headless CMS がカスタムプレビュー URLの設定をサポートしていることを前提としています。サポートしていない場合でも、この方法でプレビュー URL を保護できますが、プレビュー URL を手動で作成してアクセスする必要があります。

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

次に、headless 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 を使用する必要があります。

headless 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)
}

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

ステップ 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.previewcontext.previewData に基づいて異なるデータをフェッチするように getStaticProps を更新できます。

たとえば、headless 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' : ''}`)
  // ...
}

以上です! headless CMS から、または手動でプレビュー API ルート(secretslug を使用)にアクセスすると、プレビューコンテンツが表示されるはずです。また、公開せずにドラフトを更新すると、ドラフトをプレビューできます。

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

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

詳細

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

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

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ルートで動作します

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

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

next buildごとに固有です

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

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