Menu

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

注意: この機能はDraft Modeに置き換わりました。

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

Static GenerationはページがヘッドレスなCMSからデータを取得する場合に便利です。ただし、ヘッドレスなCMSで下書きを作成していて、その下書きをすぐにページでプレビューしたい場合には理想的ではありません。ビルド時ではなく、リクエスト時にこれらのページを次のようにレンダリングし、公開済みコンテンツの代わりに下書きコンテンツを取得したいでしょう。Static Generationをこの特定のケースだけバイパスしたいはずです。

Next.jsにはPreview Modeという機能があり、この問題を解決します。以下は使用方法の説明です。

ステップ1:Preview APIルートを作成してアクセスする

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

最初にPreview APIルートを作成します。任意の名前を付けることができます。例えば、pages/api/preview.js(TypeScriptを使用している場合は.ts)。

このAPIルートでは、レスポンスオブジェクトのsetPreviewDataを呼び出す必要があります。setPreviewDataの引数はオブジェクトである必要があり、getStaticPropsで使用できます(詳細は後述)。ここでは{}を使用します。

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

res.setPreviewDataはブラウザにクッキーを設定し、Preview Modeをオンにします。これらのクッキーを含むNext.jsへのリクエストはPreview Modeと見なされ、静的に生成されたページの動作が変わります(詳細は後述)。

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

pages/api/preview.js
// ブラウザから手動でテストするための簡単な例です。
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として指定します。これは、Preview 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}のようにします。

最後に、Preview 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: 'Invalid token' })
  }
 
  // ヘッドレスなCMSから取得して、提供された`slug`が存在することを確認します
  // getPostBySlugは、ヘッドレスなCMSへの必要な取得ロジックを実装します
  const post = await getPostBySlug(req.query.slug)
 
  // slugが存在しない場合、Preview Modeが有効にならないようにします
  if (!post) {
    return res.status(401).json({ message: 'Invalid slug' })
  }
 
  // クッキーを設定してPreview Modeを有効にします
  res.setPreviewData({})
 
  // 取得したポストのパスにリダイレクトします
  // req.query.slugにリダイレクトしません。これはオープンリダイレクト脆弱性につながる可能性があります
  res.redirect(post.slug)
}

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

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

次のステップは、Preview ModeをサポートするためにgetStaticPropsを更新することです。

Preview Modeクッキー(res.setPreviewData経由で設定)が設定されている状態でgetStaticPropsを持つページをリクエストすると、getStaticPropsビルド時ではなくリクエスト時に呼び出されます。

さらに、以下のcontextオブジェクトと共に呼び出されます。

  • context.previewtrueになります。
  • context.previewDatasetPreviewDataに使用された引数と同じになります。
export async function getStaticProps(context) {
  // Preview Modeクッキーが設定されたままこのページをリクエストした場合:
  //
  // - context.previewはtrueになります
  // - context.previewDataは
  //   setPreviewDataに使用された引数と同じになります。
}

Preview APIルートでres.setPreviewData({})を使用したため、context.previewData{}になります。必要に応じて、Preview APIルートからgetStaticPropsへセッション情報を渡すのに使用できます。

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

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

context.previewや/またはcontext.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' : ''}`)
  // ...
}

これだけです!Preview APIルートに(secretslugで)アクセスするか、手動でアクセスするかしてから、Preview Modeクッキーが設定されたままアクセスすると、プレビューコンテンツを見ることができるはずです。下書きを公開せずに更新した場合、下書きをプレビューできるはずです。

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

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

さらに詳しく

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

Preview Mode期間を指定する

setPreviewDataは、オプションの2番目のパラメーターを受け取ります。これは、オプションオブジェクトである必要があります。以下のキーを受け入れます。

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

Preview Modeクッキーをクリアする

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

Preview Modeクッキーを手動でクリアするには、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を使用して呼び出している場合は、リンククリ前にクリア処理を呼び出すのを防ぐため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で動作する

Preview ModeはgetServerSidePropsでも動作します。previewpreviewDataを含むcontextオブジェクトでも利用可能になります。

補足: Preview Modeを使用する場合は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でローカルにPreview Modeをテストするには、ブラウザがサードパーティクッキーとローカルストレージアクセスを許可する必要があります。