All Systems Operational
Powered By
profound-logo
profound-logoProfound CMS
⌘K
Admin

編集モード対応の静的レンダリング

CMSの編集モード機能を維持しながら、ドキュメントサイトが高速な静的ページ読み込みを実現する方法を説明します

Continue Reading
Previous‹管理パネルプロキシの設定Nextテンプレートビルダーでのスクリプティング›

ハイブリッド

レンダラー・プロジェクトパラメトリックルーティングコンポーネントの種類Sse管理パネルプロキシの設定編集モード対応の静的レンダリングテンプレートビルダーでのスクリプティングCreate Profound Next

ヘッドレス

クイックスタートJsonとクロードコードComponent Zod Pull

Mcp

Mcp

CMS機能

Feat Docs Templateテンプレートビルダー機能Feat トランスレーターFeat 組織

やる気

私たちのアプローチ

専門用語

ハイブリッド対ヘッドレス

このガイドでは、ドキュメントサイトがCMSの編集モード機能を維持しつつ、高速な静的ページ読み込みを実現する方法を説明します。

二重ルートアーキテクチャ

同一コンテンツに対して2つのルートを使用しています:

app/
├── [...slug]/page.tsx          # 本番: force-static(高速)
└── preview/[...slug]/page.tsx  # 編集モード: force-dynamic(searchParamsを読み取る)

プロキシミドルウェアが編集モードのリクエストを透過的にリライトします:

ユーザーリクエスト: /en/headless/quickstart?edit_mode=true
       ↓
プロキシが ?edit_mode=true を検知
       ↓
内部的に /preview/en/headless/quickstart?edit_mode=true へリライト
       ↓
動的ルートが編集用ラッパー付きでレンダリング

ユーザーがURLで /preview を目にすることは決してありません — これは内部リライトです。

仕組み

本番ルート(デフォルト)

// app/[...slug]/page.tsx
export const dynamic = 'force-static';
export const revalidate = 60;

export default async function Page({ params }) {
  // searchParams なし - ページは完全な静的生成
  return
  • ページはビルド時に事前レンダリングされます
  • CDNエッジキャッシュから即座に配信されます
  • 保険として60秒ごとに再検証(ISR)されます

編集モードルート

// app/preview/[...slug]/page.tsx
export const dynamic = 'force-dynamic';

export default async function PreviewPage({ params, searchParams }) {
  // searchParams が利用可能 - edit_mode を検知できる
  return <ParametricRoutePage params={params} 
  • リクエストのたびにレンダリングされます
  • URLの ?edit_mode=true を読み取ることができます
  • CMSの編集可能ラッパーとブロックアウトライン付きでレンダリングします

プロキシによるリライト

// src/proxy.ts
export const proxy = async (request: NextRequest) => {
  const { pathname, searchParams } = request.nextUrl;

  const editMode = searchParams.get('edit_mode');

パフォーマンス比較

シナリオレスポンスタイムレンダリング
本番ページ(キャッシュ済み)約50〜100msCDNからの静的配信
本番ページ(キャッシュ陳腐化)約50〜100ms + バックグラウンド更新静的配信後にISR
編集モード約500〜1500ms動的SSR

ISR(増分静的再生成)を理解する

revalidate = 60 の設定はISRを有効化します。これはページが60秒ごとに再計算されるという意味ではありません。

ISRは stale-while-revalidate(期限切れでも即時提供しつつ裏で更新) パターンを利用します:

リクエストが来る:
  → キャッシュページが60秒未満? → キャッシュから提供(即時)
  → キャッシュページが60秒超過? → 期限切れキャッシュを提供(即時)
                                + 次回リクエスト向けにバックグラウンドで再検証

リクエストがなければ = 再計算は一切起こりません。

例:

  • ページが午前10:00にキャッシュされる
  • 午後3:00まで訪問者なし
  • 午後3:00の最初の訪問者は期限切れキャッシュを即時取得
  • バックグラウンド更新が走り、次の訪問者は新鮮なコンテンツを取得

60秒のウィンドウは安全策です。理想的には、CMSコンテンツが更新された際にWebhook経由でオンデマンド再検証を利用します。

ルートの使い分け

ユースケースURL使用ルート
通常閲覧/en/headless/quickstart静的
CMSテンプレートビルダー/en/headless/quickstart?edit_mode=trueプレビュー(リライト経由)
AIブロックプレビュー/en/headless/quickstart?ai_preview=1プレビュー(リライト経由)

なぜ動的レンダリングだけにしないのか?

単純なアプローチ(force-staticなし、プレビュールートなし)でも動作しますが、遅くなります:

// 単純なアプローチ - 動作はするがリクエストごとに約1〜2秒
export default async function Page({ params, searchParams }) {
  return <ParametricRoutePage params={params} searchParams={searchParams} />;
}

これはリクエストのたびに動的レンダリングします。ページ数の多いドキュメントサイトでは次のような結果になります:

  • ページごとのコールドスタート
  • CDNエッジキャッシュの恩恵なし
  • ミリ秒ではなく1〜2秒の読み込み時間

二重ルートアーキテクチャにより、閲覧者には静的な速度を、編集者には動的な機能を提供するという両立が可能になります。

<
ParametricRoutePage
params
={
params
} />;
}
searchParams
={
searchParams
} />;
}
const
aiPreview
=
searchParams.
get
(
'ai_preview'
);
// edit_mode または ai_preview が存在し、かつ /preview で始まっていない場合はプレビュールートへリライト
if ((editMode === 'true' || editMode === '1' || aiPreview) && !pathname.startsWith('/preview')) {
const url = request.nextUrl.clone();
url.pathname = `/preview${pathname}`;
return NextResponse.rewrite(url);
}
return NextResponse.next();
};