プレビューモード
注: この機能はドラフトモードに置き換えられました。
例
- WordPressの例 (デモ)
- DatoCMSの例 (デモ)
- TakeShapeの例 (デモ)
- Sanityの例 (デモ)
- Prismicの例 (デモ)
- Contentfulの例 (デモ)
- Strapiの例 (デモ)
- Preprの例 (デモ)
- Agility CMSの例 (デモ)
- Cosmicの例 (デモ)
- ButterCMSの例 (デモ)
- Storyblokの例 (デモ)
- GraphCMSの例 (デモ)
- Kontentの例 (デモ)
- Umbraco Heartcoreの例 (デモ)
- Plasmicの例 (デモ)
- Enterspeedの例 (デモ)
- Makeswiftの例 (デモ)
ページドキュメントとデータフェッチングドキュメントでは、getStaticProps
とgetStaticPaths
を使用してビルド時にページを事前レンダリングする方法(静的生成)について説明しました。
静的生成は、ヘッドレスCMSからデータを取得するページに有効です。しかし、ヘッドレスCMSで下書きを書いている際に、その下書きをすぐにプレビューしたい場合には理想的ではありません。このような場合、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: '無効なトークンです' })
}
// ヘッドレスCMSに問い合わせて、指定された`slug`が存在するか確認
// getPostBySlugはヘッドレスCMSへの必要なフェッチロジックを実装します
const post = await getPostBySlug(req.query.slug)
// slugが存在しない場合、プレビューモードを有効にしない
if (!post) {
return res.status(401).json({ message: '無効なslugです' })
}
// クッキーを設定してプレビューモードを有効化
res.setPreviewData({})
// 取得した投稿のパスにリダイレクト
// req.query.slugにリダイレクトしない(オープンリダイレクトの脆弱性につながる可能性があるため)
res.redirect(post.slug)
}
成功すると、ブラウザはプレビューしたいパスにプレビューモードのクッキーが設定された状態でリダイレクトされます。
ステップ2: getStaticProps
の更新
次のステップは、getStaticProps
を更新してプレビューモードをサポートすることです。
プレビューモードのクッキーが設定された状態(res.setPreviewData
経由)でgetStaticProps
を持つページをリクエストすると、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から(secret
とslug
を使用して)プレビューAPIルートにアクセスするか、手動でアクセスすると、プレビューコンテンツを確認できるはずです。また、公開せずに下書きを更新しても、その下書きをプレビューできるはずです。
ヘッドレスCMSにこれをプレビューURLとして設定するか、手動でアクセスすると、プレビューを確認できます。
https://<your-site>/api/preview?secret=<token>&slug=<path>
詳細
知っておくと良い: レンダリング中、
next/router
はisPreview
フラグを公開します。詳細はrouterオブジェクトのドキュメントをご覧ください。
プレビューモードの期間を指定する
setPreviewData
はオプションの第二引数を取ります。これはオプションオブジェクトである必要があり、以下のキーを受け入れます:
maxAge
: プレビューセッションが持続する秒数を指定します。path
: クッキーが適用されるパスを指定します。デフォルトは/
で、すべてのパスでプレビューモードが有効になります。
setPreviewData(data, {
maxAge: 60 * 60, // プレビューモードのクッキーは1時間で期限切れになります
path: '/about', // プレビューモードのクッキーは/aboutのパスに適用されます
})
プレビューモードのクッキーをクリアする
デフォルトでは、プレビューモードのクッキーに有効期限が設定されていないため、プレビューセッションはブラウザを閉じると終了します。
プレビューモードのクッキーを手動でクリアするには、clearPreviewData()
を呼び出すAPIルートを作成します:
export default function handler(req, res) {
res.clearPreviewData({})
}
その後、/api/clear-preview-mode-cookies
にリクエストを送信してAPIルートを呼び出します。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
でも機能します。context
オブジェクトにpreview
とpreviewData
が含まれます。
知っておくと良い: プレビューモードを使用する場合、
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経由でローカルでプレビューモードをテストするには、ブラウザでサードパーティのクッキーとローカルストレージへのアクセスを許可する必要があります。