プレビューモード

このドキュメントは、Next.jsバージョン9.3以上を対象としています。Next.jsの古いバージョンを使用している場合は、 以前のドキュメント を参照してください。

ページのドキュメントデータ取得のドキュメントで、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 の引数はオブジェクトである必要があり、 getStatciProps (これについては後ほど詳しく説明します)によって使用することが出来ます。ここでは {} を使用します。

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

res.setPreviewData はプレビューモードを実行するブラウザにいくつかの Cookie を設定します。これらの Cookie を含む Next.js へのリクエストはpreview modeと判断され、静的に生成されたページの動作が変更されます(これについては後ほど詳しく説明します)。

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

// ブラウザから手動でテストするための簡単なサンプルです。
// もしこのファイルがpages/api/preview.jsに置かれているならば、
// ブラウザで、 /api/preview を開いてください。
export default function handler(req, res) => {
  res.setPreviewData({})
  res.end('Preview mode enabled')
}

ブラウザのデベロッパーツールを使用すると、 __prerender_bypass__next_preview_data という Cookie がリクエストに設定されていることに気づくでしょう。

ヘッドレス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 に変数を含めることも出来るので、これにより &slug=/posts/{entry.fields.slug} のように、CMS のデータに基づいて動的に <path> を設定できるようになります。

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

  • シークレットトークンが一致し、 slug パラメータが存在することを確認してください(存在しない場合、リクエストは失敗するはずです)。
  • res.setPreviewData を呼び出してください.
  • それから、ブラウザを slug で指定したパスにリダイレクトします。(次の例では307リダイレクトを使用しています)
export default async (req, res) => {
  // シークレットトークンと次のパラメーターを確認してください。
  // このシークレットトークンはAPIルートとCMSだけが知っている必要があります。
  if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  // 提供された `slug` が存在しているかどうか確認するため、ヘッドレスCMSをフェッチします。
  // getPostBySlugはヘッドレスCMSへの必要なフェッチロジックを実装します。
  const post = await getPostBySlug(req.query.slug)

  // slugが存在しない場合、プレビューモードを有効にしないようにしましょう。
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }

  // Cookiesを設定し、プレビューモードを有効にします。
  res.setPreviewData({})

  // フェッチされた投稿にパスをリダイレクトします。
  // オープンリダイレクトの脆弱性につながる可能性があるため、req.query.slugにリダイレクトしません。
  res.redirect(post.slug)
}

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

ステップ2 getStaticProps を更新

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

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

さらに、次のような context オブジェクトを引数に getStaticProps が呼ばれます:

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

プレビューAPI ルートで res.setPreviewData({}) を使用したため、 context.previewData{} になります。 これを用いて、プレビューAPI ルートから getStaticProps に、必要であればセッション情報を渡すことが出来ます。

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

プレビューデータを取得する

context.previewcontext.previewData に基づいて異なるデータを取得するように getStaticProps を更新することが出来ます。

例えば、ヘッドレス CMS は下書き投稿のための異なる API エンドポイントを持っている場合があります。その場合、 context.preview を使って以下のように API エンドポイント URL を変更することが出来ます。

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

以上です!ヘッドレス CMS から、または手動で、( secretslug を使用して)プレビューAPI ルートにアクセスすると、プレビューコンテンツを表示することが出来ます。また公開せずに下書きを更新すると、下書きをプレビューすることが出来るはずです。

# これをヘッドレスCMSのプレビューURLとして設定するか、手動でアクセスすることで、
# プレビューを見ることが出来るようになります。
https://<your-site>/api/preview?secret=<token>&slug=<path>

もっと詳しく

プレビューモードのCookieを削除する

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

手動でプレビューの Cookie を削除するには、 clearPreviewData を呼ぶ API ルートを作成し、この API ルートにアクセスします。

export default function handler(req, res) => {
  // プレビューモードのCookieを削除します。
  // この関数は引数を受け取りません。
  res.clearPreviewData()
  // ...
}

注意: Link コンポーネントを用いてこのルートを呼び出す場合には、prefetch={false} を渡して clearPreviewData がプリフェッチ中に実行されることを防ぐ必要があります。

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

setPreviewData はオプションで 2 番目のパラメータを指定でき、これが設定に関するオブジェクトになります。次のキーを受け取ります:

  • maxAge: プレビューセッションの継続時間を指定します。
setPreviewData(data, {
  maxAge: 60 * 60, // プレビューモードのCookieの期限が1時間になります。
})

previewData のサイズ制限

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

getServerSideProps の動作

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

APIルートと動かす

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

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

next build ごとに単一

next build を実行するたびに、バイパス Cookie の値と暗号化された previewData の秘密鍵が変更されます。これにより、バイパス Cookie を推測出来ないようにします。

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

もっと詳しく知る

次のページも役に立つでしょう。