Next.jsのプレビューモードでコンテンツをプレビューする方法
注意: この機能はドラフトモードに置き換えられています。
例
- Agility CMS の例 (デモ)
- Builder.io の例 (デモ)
- ButterCMS の例 (デモ)
- Contentful の例 (デモ)
- Cosmic の例 (デモ)
- DatoCMS の例 (デモ)
- DotCMS の例 (デモ)
- Drupal の例 (デモ)
- Enterspeed の例 (デモ)
- GraphCMS の例 (デモ)
- Keystone の例 (デモ)
- Kontent.ai の例 (デモ)
- Makeswift の例 (デモ)
- Plasmic の例 (デモ)
- Prepr の例 (デモ)
- Prismic の例 (デモ)
- Sanity の例 (デモ)
- Sitecore XM Cloud の例 (デモ)
- Storyblok の例 (デモ)
- Strapi の例 (デモ)
- TakeShape の例 (デモ)
- Tina の例 (デモ)
- Umbraco の例 (デモ)
- Umbraco Heartcore の例 (デモ)
- Webiny の例 (デモ)
- WordPress の例 (デモ)
- ブログスターターの例 (デモ)
ページドキュメントとデータフェッチングドキュメントでは、getStaticProps
とgetStaticPaths
を使用してビルド時にページを事前レンダリングする方法(静的生成)について説明しました。
静的生成は、ページがヘッドレス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
はブラウザにいくつかのクッキーを設定し、プレビューモードを有効にします。これらのクッキーを含むNext.jsへのリクエストはプレビューモードとみなされ、静的に生成されたページの動作が変わります(詳細は後述)。
これを手動でテストするには、以下のようなAPIルートを作成し、ブラウザから手動でアクセスすることができます:
// ブラウザから手動でテストするための簡単な例
export default function handler(req, res) {
res.setPreviewData({})
res.end('プレビューモードが有効になりました')
}
ブラウザの開発者ツールを開き、/api/preview
にアクセスすると、このリクエストで__prerender_bypass
と__next_preview_data
クッキーが設定されていることがわかります。
ヘッドレス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に変数を含められる場合があり、<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: '無効なスラグです' })
}
// クッキーを設定してプレビューモードを有効にする
res.setPreviewData({})
// フェッチしたポストのパスにリダイレクト
// オープンリダイレクト脆弱性につながる可能性があるため、req.query.slugにリダイレクトしない
res.redirect(post.slug)
}
成功すると、ブラウザはプレビューしたいパスにリダイレクトされ、プレビューモードのクッキーが設定されます。
ステップ2:getStaticProps
を更新する
次のステップは、プレビューモードをサポートするためにgetStaticProps
を更新することです。
プレビューモードのクッキーが設定されている状態でgetStaticProps
を持つページをリクエストすると(res.setPreviewData
経由)、getStaticProps
はビルド時ではなくリクエスト時に呼び出されます。
さらに、以下のcontext
オブジェクトで呼び出されます:
context.preview
はtrue
になります。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.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' : ''}`)
// ...
}
これで完了です!ヘッドレスCMSから(または手動で)プレビューAPIルート(secret
とslug
を指定)にアクセスすると、プレビューコンテンツが表示されるはずです。そして、下書きを公開せずに更新した場合でも、その下書きをプレビューできるはずです。
ヘッドレスCMSでこれをプレビューURLとして設定するか、手動でアクセスすると、プレビューを確認できるはずです。
https://<your-site>/api/preview?secret=<token>&slug=<path>
詳細情報
補足: レンダリング中、
next/router
はisPreview
フラグを公開しています。詳細はrouterオブジェクトのドキュメントを参照してください。
プレビューモードの期間を指定する
setPreviewData
はオプションの2番目のパラメータとしてオプションオブジェクトを受け取ります。以下のキーを受け付けます:
maxAge
: プレビューセッションが持続する秒数を指定します。path
: クッキーが適用されるパスを指定します。デフォルトは/
で、すべてのパスでプレビューモードが有効になります。
setPreviewData(data, {
maxAge: 60 * 60, // プレビューモードのクッキーは1時間で期限切れ
path: '/about', // プレビューモードのクッキーは/aboutを含むパスに適用
})
プレビューモードのクッキーをクリアする
デフォルトでは、プレビューモードのクッキーに有効期限が設定されていないため、ブラウザを閉じるとプレビューセッションは終了します。
プレビューモードのクッキーを手動でクリアするには、clearPreviewData()
を呼び出すAPIルートを作成します:
export default function handler(req, res) {
res.clearPreviewData({})
}
その後、APIルートを呼び出すために/api/clear-preview-mode-cookies
にリクエストを送信します。next/link
を使用してこのルートを呼び出す場合は、リンクのプリフェッチ中にclearPreviewData
が呼び出されるのを防ぐためにprefetch={false}
を渡す必要があります。
setPreviewData
呼び出しでパスが指定されている場合は、clearPreviewData
にも同じパスを渡す必要があります:
export default function handler(req, res) {
const { path } = req.query
res.clearPreviewData({ path })
}
previewData
のサイズ制限
setPreviewData
にオブジェクトを渡してgetStaticProps
で利用できるようにすることができます。ただし、データはクッキーに保存されるため、サイズ制限があります。現在、プレビューデータは2KBに制限されています。
getServerSideProps
でも動作します
プレビューモードはgetServerSideProps
でも動作します。preview
とpreviewData
を含むcontext
オブジェクトでも利用できます。
補足: プレビューモードを使用する場合、
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を介してローカルでプレビューモードをテストするには、ブラウザがサードパーティのクッキーとローカルストレージへのアクセスを許可する必要があります。