メインコンテンツまでスキップ

プレビュー(Preview)

Page summary:

プレビューはコンテンツマネージャーとフロントをつなぎ、公開前に更新内容を確認できます。このページではプレビュー URL を組み立てる設定手順を扱います。

プレビュー機能により、Strapi 管理パネルからフロントエンドアプリの表示を確認できます。コンテンツマネージャーの編集ビューでコンテンツを変えたとき、最終的な見え方を把握するのに役立ちます。

IDENTITY CARD
プラン
無料で Preview 利用可
Live Preview は CMS Growth / Enterprise プランのみ。
ロールと権限
Roles > Plugins - Users & Permissions で Read 権限
有効化
config/admin で設定
環境
開発・本番の両方で利用できます
コンテンツのプレビューコンテンツのプレビュー

設定

メモ
  • 次の環境変数を .env に定義し、例の値を実環境に合わせて置き換えてください。

    CLIENT_URL=https://your-frontend-app.com
    PREVIEW_SECRET=your-secret-key

    PREVIEW_SECRET は任意ですが、Next.js の draft モードでは必要です。

  • Strapi プロジェクト用のフロントエンドアプリがすでに用意されていること。

設定の構成

プレビュー機能の設定は config/adminpreview オブジェクトに書き、主に次の 3 要素で構成されます。

有効化フラグ

プレビュー機能のオン/オフです。

config/admin.ts|js
// …
preview: {
enabled: true,
// …
}
// …

許可オリジン(allowed origins)

プレビューを許可するドメインを制御します。

config/admin.ts|js
// …
preview: {
enabled: true,
config: {
allowedOrigins: env("CLIENT_URL"), // Usually your frontend application URL
// …
}
}
// …

プレビューハンドラー

プレビューのロジックと URL 生成を担います。次の例では uid がコンテンツタイプ識別子(例: api::article.articleplugin::my-api.my-content-type)です。

config/admin.ts|js
// …
preview: {
enabled: true,
config: {
// …
async handler(uid, { documentId, locale, status }) {
const document = await strapi.documents(uid).findOne({ documentId });
const pathname = getPreviewPathname(uid, { locale, document });

return `${env('PREVIEW_URL')}${pathname}`
},
}
}
// …

URL 生成ロジックの例は、次の基本実装ガイドにあります。

下書きエントリーのプレビュー

フロントが下書き/公開版のどちらを取るかはフレームワーク次第です。代表的には次の 3 パターンがあります。

  • クエリパラメータ — 例: /your-path?preview=trueNuxt の preview モードなど)
  • 専用プレビュールートへリダイレクト — 例: /preview?path=your-pathNext.js の draft モード など)
  • 別ドメイン — 例: preview.mysite.com/your-path

コンテンツタイプで Draft & Publish が有効なら、プレビューハンドラー内で Strapi の status パラメーターを直接使う方法もあります。例:

async handler(uid, { documentId, locale, status }) {
const document = await strapi.documents(uid).findOne({ documentId });
const pathname = getPreviewPathname(uid, { locale, document });
if (status === 'published') {
// return the published version
}
// return the draft version
},

Next.js の draft モードを使う具体例は基本実装ガイドの手順 3にあります。

基本実装ガイド

次の手順でコンテンツタイプにプレビュー機能を足せます。

1. [Strapi] プレビュー設定を作成

/config/admin.ts を新規作成するか、次の骨組みで更新します。

config/admin.ts
export default ({ env }) => ({
// Other admin-related configurations go here
// (see docs.strapi.io/cms/configurations/admin-panel)
preview: {
enabled: true,
config: {
allowedOrigins: env('CLIENT_URL'),
async handler (uid, { documentId, locale, status }) => {
// Handler implementation coming in step 3
},
},
},
});

2. [Strapi] URL 生成ロジックを追加

getPreviewPathname で URL を組み立てます。次の例は Strapi デモ Launchpad 由来です。

config/admin.ts
// Function to generate preview pathname based on content type and document
const getPreviewPathname = (uid, { locale, document }): string => {
const { slug } = document;

// Handle different content types with their specific URL patterns
switch (uid) {
// Handle pages with predefined routes
case "api::page.page":
switch (slug) {
case "homepage":
return `/${locale}`; // Localized homepage
case "pricing":
return "/pricing"; // Pricing page
case "contact":
return "/contact"; // Contact page
case "faq":
return "/faq"; // FAQ page
}
// Handle product pages
case "api::product.product": {
if (!slug) {
return "/products"; // Products listing page
}
return `/products/${slug}`; // Individual product page
}
// Handle blog articles
case "api::article.article": {
if (!slug) {
return "/blog"; // Blog listing page
}
return `/blog/${slug}`; // Individual article page
}
default: {
return null;
}
}
};

// … main export (see step 3)
Note

プレビューが不要なコンテンツタイプは defaultnull を返します。サイトメタデータだけのグローバルシングル型など、対応するフロントページがない場合が該当します。ハンドラーが null を返すと管理パネルにプレビュー UI は出ません。コンテンツタイプごとにプレビューの有無を切り替える方法です。

3. [Strapi] ハンドラー実装を追加

手順 1 の基本設定に、手順 2 の URL 生成と次のハンドラー処理を足して完成させます。

config/admin.ts
const getPreviewPathname = (uid, { locale, document }): string => {
// … as defined in step 2
};

// Main configuration export
export default ({ env }) => {
// Get environment variables
const clientUrl = env("CLIENT_URL"); // Frontend application URL
const previewSecret = env("PREVIEW_SECRET"); // Secret key for preview authentication

return {
// Other admin-related configurations go here
// (see docs.strapi.io/cms/configurations/admin-panel)
preview: {
enabled: true, // Enable preview functionality
config: {
allowedOrigins: clientUrl, // Restrict preview access to specific domain
async handler(uid, { documentId, locale, status }) {
// Fetch the complete document from Strapi
const document = await strapi.documents(uid).findOne({ documentId });

// Generate the preview pathname based on content type and document
const pathname = getPreviewPathname(uid, { locale, document });

// Disable preview if the pathname is not found
if (!pathname) {
return null;
}

// Use Next.js draft mode passing it a secret key and the content-type status
const urlSearchParams = new URLSearchParams({
url: pathname,
secret: previewSecret,
status,
});
return `${clientUrl}/api/preview?${urlSearchParams}`;
},
},
},
};
};

4. [フロントエンド] プレビュールートを用意

フロント側のプレビュールートは、使っているフレームワークに強く依存します。

例: Next.js draft modeNuxt preview mode など、各公式ドキュメントにフロント実装の詳細があります。

Next.js の場合の基本例(Launchpad)は次のとおりです。

/next/api/preview/route.ts
import { draftMode } from "next/headers";
import { redirect } from "next/navigation";

export async function GET(request: Request) {
// Parse query string parameters
const { searchParams } = new URL(request.url);
const secret = searchParams.get("secret");
const url = searchParams.get("url");
const status = searchParams.get("status");

// Check the secret and next parameters
// This secret should only be known to this route handler and the CMS
if (secret !== process.env.PREVIEW_SECRET) {
return new Response("Invalid token", { status: 401 });
}

// Enable Draft Mode by setting the cookie
if (status === "published") {
draftMode().disable();
} else {
draftMode().enable();
}

// Redirect to the path from the fetched post
// We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities
redirect(url || "/");
}

5. [フロントエンド] 埋め込みを許可

Strapi 側の allowedOrigins で、管理パネルが iframe 内にフロントを読み込めます。埋め込みは双方向のため、フロント側でも Strapi 管理パネルからの埋め込みを許可する必要があります。

フロントには CSP の frame-ancestors などのヘッダーが必要です。設定はサイトの構成次第です。Next.js ではミドルウェアで設定する例があります(Next.js のドキュメント)。

6. [フロントエンド] 下書き向けにデータ取得を合わせる

プレビュー基盤ができたら、データ取得を下書きに対応させます。

  1. draft モードが有効かどうかを見るユーティリティを用意または改修する
  2. 必要に応じて API 呼び出しに下書き用の status パラメーターを足す

次は Next.js フロントで下書きを意識して取得する例です(Launchpad)。

import { draftMode } from "next/headers";
import qs from "qs";

export default async function fetchContentType(
contentType: string,
params: Record = {}
): Promise {
// Check if Next.js draft mode is enabled
const { isEnabled: isDraftMode } = await draftMode();

try {
const queryParams = { ...params };
// Add status=draft parameter when draft mode is enabled
if (isDraftMode) {
queryParams.status = "draft";
}

const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(
`Failed to fetch data from Strapi (url=${url}, status=${response.status})`
);
}
return await response.json();
} catch (error) {
console.error("Error fetching content:", error);
throw error;
}
}

このユーティリティをページコンポーネントから呼び、プレビュー状態に応じて下書きまたは公開版を取得できます。

// ページコンポーネント内の例:
const pageData = await fetchContentType('api::page.page', {
// ほかのクエリパラメーター
});

Live Preview の実装

GrowthThis feature is available with a Growth plan. EnterpriseThis feature is available with an Enterprise plan.

基本の Preview を設定したあと、Live Preview で体験を強化できます。

window メッセージ

Live Preview は管理画面とフロントのあいだで通信し、より双方向にします。windowpostMessage() API でイベントを送ります。

アプリ側ではリスナーを追加します。全ページで効かせるため、アプリ全体を包むレイアウトに置くのがおすすめです。メッセージはふるいにかけ、Strapi 由来のものだけに反応します。

受け取るメッセージは主に次の 2 種類です。

  • strapiUpdate … コンテンツ更新が DB に保存されたときに Strapi から送られます。最新内容を取り直してプレビューを更新するタイミングです。Next.js では iframe を更新するのに `router.refresh()` が推奨です。
  • strapiScript … Live Preview 用スクリプトです。ページの <head> に注入します。プレビュー内の編集可能な領域を強調表示し、ダブルクリックで Strapi に戻すメッセージを送ります。

このスクリプトを受け取るには、フロントが準備できたことを Strapi に伝えます。親ウィンドウへ previewReadypostMessage します。

ひとまとまりの例として、グローバルレイアウトへ足せるコンポーネントのイメージは次のとおりです。

next/app/path/to/your/front/end/logic.jsx
'use client';

export default function LivePreview() {
// …
const router = useRouter();

useEffect(() => {
const handleMessage = async (message) => {
const { origin, data } = message;

if (origin !== process.env.NEXT_PUBLIC_API_URL) {
return;
}

if (data.type === 'strapiUpdate') {
router.refresh();
} else if (data.type === 'strapiScript') {
const script = window.document.createElement('script');
script.textContent = data.payload.script;
window.document.head.appendChild(script);
}
};

// Add the event listener
window.addEventListener('message', handleMessage);

// Let Strapi know we're ready to receive the script
window.parent?.postMessage({ type: 'previewReady' }, '*');

// Remove the event listener on unmount
return () => {
window.removeEventListener('message', handleMessage);
};
}, [router]);

return null;
}
Next.js のキャッシュ

Next.js では キャッシュの扱い により、追加手順が要ることがあります。クライアントからサーバーへ API を呼び、サーバー側で再検証する、といった方法が必要になる場合があります。詳しくは Next.js のドキュメント(例: revalidatePath())を参照してください。


コンテンツソースマップ

Live Preview は、Strapi のフィールドに対応するフロント上の箇所を特定できます。文字列コンテンツ(テキストフィールドなど)に、不可視の文字でメタデータを埋め込む コンテンツソースマップ で実現します。エンコード/デコードには @vercel/stega を使います。

Content API のレスポンスにこのメタデータを載せるには、strapi-encode-source-maps ヘッダーを true にします。データ取得ユーティリティで付与します。プレビュー表示中だけに限定してください。

Next.js では next/headersdraftMode() で draft モードかどうか判定し、API 呼び出しすべてにヘッダーを設定する例は次のとおりです。

import { draftMode } from "next/headers";
import qs from "qs";

export default async function fetchContentType(
contentType: string,
params: Record = {}
): Promise {
// Check if Next.js draft mode is enabled
const { isEnabled: isDraftMode } = await draftMode();

try {
const queryParams = { ...params };
// Add status=draft parameter when draft mode is enabled
if (isDraftMode) {
queryParams.status = "draft";
}

const url = `${baseURL}/${contentType}?${qs.stringify(queryParams)}`;
const response = await fetch(url, {
headers: {
// Enable content source maps in preview mode
"strapi-encode-source-maps": isDraftMode ? "true" : "false",
},
});
if (!response.ok) {
throw new Error(
`Failed to fetch data from Strapi (url=${url}, status=${response.status})`
);
}
return await response.json();
} catch (error) {
console.error("Error fetching content:", error);
throw error;
}
}

使い方

利用場所: Content Manager — 対象コンテンツタイプの編集ビュー

Preview と Live Preview

CMS プランによって体験が変わります。

プレビューを正しく設定すると、コンテンツマネージャーの編集ビュー の右側に Open preview が表示されます。クリックすると、フロントでの見え方に近いプレビューが Strapi 管理パネル内に開きます。

コンテンツのプレビューコンテンツのプレビュー

プレビューが開いたあとにできること:

  • 左上の閉じる で編集ビューに戻る
  • プレビュー上部のドロップダウンでデスクトップ/モバイル表示を切り替える
  • 下書き版と公開版のプレビューを切り替える(コンテンツタイプで Draft & Publish が有効な場合)
  • 右上のリンク でプレビュー用 URL をコピーする(表示中のタブに応じて下書き用か公開版用かが変わります)
Note

編集ビューでは、未保存の変更があると Open preview は無効になります。保存してから再度プレビューできます。

Live Preview

GrowthThis feature is available with a Growth plan. EnterpriseThis feature is available with an Enterprise plan.

Live Preview は、有料 CMS プランで利用できる強化版の Preview です。

Free プランの Preview に加え、次が利用できます。

  • Side Editor で、エントリーの編集ビューとフロントのプレビューを左右に並べて表示する。フルスクリーンと並列表示は で切り替える。
  • プレビューペインのコンテンツをダブルクリックすると、その場で編集できるポップオーバーが開き、フロント表示と Strapi の対応フィールドが同期する。
Live PreviewLive Preview
試験的機能

この機能は試験的です。フィードバックissue の共有を歓迎します。

現時点の Live Preview には次の制限があります。

  • Blocks フィールドは検出されず、Side Editor で変えてもプレビューに反映されません。更新後に Save すれば動作は続けられます。
  • メディアアセットと Dynamic Zone 内のフィールドは扱われません。