Menu

プレビューモード

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

ページのドキュメントデータフェッチングのドキュメントで、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: '無効なトークン' })
  }
 
  // 提供された`slug`が存在するかどうかをヘッドレスCMSにフェッチ
  // 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 を更新することです。

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

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

  • context.previewtrue になります。
  • context.previewData は、setPreviewData に使用された引数と同じになります。
export async function getStaticProps(context) {
  // プレビューモードのクッキーが設定されたページをリクエストする場合:
  //
  // - 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 の場合、APIエンドポイントに "/preview" を追加し、
  // 公開データの代わりに下書きデータをリクエストします。これは
  // 使用しているヘッドレスCMSによって異なります。
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

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

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

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

さらに詳しく

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

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

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

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

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

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

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

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

次に、APIルートを呼び出すために /api/clear-preview-mode-cookies にリクエストを送信します。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 ごとにユニーク

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

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