Next.jsのPreview Modeでコンテンツをプレビューする方法
注意: この機能はDraft Modeに置き換わりました。
例
- Agility CMS Example (Demo)
- Builder.io Example (Demo)
- ButterCMS Example (Demo)
- Contentful Example (Demo)
- Cosmic Example (Demo)
- DatoCMS Example (Demo)
- DotCMS Example (Demo)
- Drupal Example (Demo)
- Enterspeed Example (Demo)
- GraphCMS Example (Demo)
- Keystone Example (Demo)
- Kontent.ai Example (Demo)
- Makeswift Example (Demo)
- Plasmic Example (Demo)
- Prepr Example (Demo)
- Prismic Example (Demo)
- Sanity Example (Demo)
- Sitecore XM Cloud Example (Demo)
- Storyblok Example (Demo)
- Strapi Example (Demo)
- TakeShape Example (Demo)
- Tina Example (Demo)
- Umbraco Example (Demo)
- Umbraco Heartcore Example (Demo)
- Webiny Example (Demo)
- WordPress Example (Demo)
- Blog Starter Example (Demo)
Pagesドキュメントとデータ取得ドキュメントでは、getStaticPropsとgetStaticPathsを使用してビルド時にページを事前レンダリング(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ルートを作成し、ブラウザからそれに手動でアクセスすることで、これを手動でテストできます。
// ブラウザから手動でテストするための簡単な例です。
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に配置されていることを前提としています。
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.previewはtrueになります。context.previewDataはsetPreviewDataに使用された引数と同じになります。
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ルートに(secretとslugで)アクセスするか、手動でアクセスするかしてから、Preview Modeクッキーが設定されたままアクセスすると、プレビューコンテンツを見ることができるはずです。下書きを公開せずに更新した場合、下書きをプレビューできるはずです。
これをヘッドレスなCMSのプレビューURLとして設定するか、手動でアクセスすると、プレビューが表示されるはずです。
https://<your-site>/api/preview?secret=<token>&slug=<path>さらに詳しく
補足: レンダリング中、
next/routerはisPreviewフラグを公開しています。詳細はルーターオブジェクトドキュメントをご覧ください。
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ルートを作成します。
export default function handler(req, res) {
res.clearPreviewData({})
}その後、/api/clear-preview-mode-cookiesにリクエストを送信してAPIルートを呼び出します。このルートをnext/linkを使用して呼び出している場合は、リンククリ前にクリア処理を呼び出すのを防ぐためprefetch={false}を渡す必要があります。
setPreviewDataの呼び出しでパスが指定されていた場合は、同じパスをclearPreviewDataに渡す必要があります。
export default function handler(req, res) {
const { path } = req.query
res.clearPreviewData({ path })
}previewDataのサイズ制限
setPreviewDataにオブジェクトを渡し、getStaticPropsで利用可能にできます。ただし、データはクッキーに保存されるため、サイズ制限があります。現在、プレビューデータは2KBに制限されています。
getServerSidePropsで動作する
Preview ModeはgetServerSidePropsでも動作します。previewとpreviewDataを含むcontextオブジェクトでも利用可能になります。
補足: Preview Modeを使用する場合は
Cache-Controlヘッダーを設定しないでください。これはバイパスできないためです。代わりにISRを使用することをお勧めします。
APIルートで動作する
APIルートはリクエストオブジェクトのpreviewとpreviewDataにアクセスできます。例えば、以下の通りです。
export default function myApiRoute(req, res) {
const isPreview = req.preview
const previewData = req.previewData
// ...
}next buildごとに一意
バイパスクッキー値とpreviewDataを暗号化するための秘密鍵は、next buildが完了すると変わります。
これにより、バイパスクッキーを推測できなくなります。
補足: HTTPでローカルにPreview Modeをテストするには、ブラウザがサードパーティクッキーとローカルストレージアクセスを許可する必要があります。