zudo-doc
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

HtmlPreview

作成 2026年3月16日更新 2026年7月24日Takeshi Takatsudo

ビューポート切り替えとソースコード表示を備えたインタラクティブなHTML/CSSプレビューコンポーネント。

<HtmlPreview> は、分離された iframe 内で HTML/CSS のライブデモを描画するコンポーネントです。ビューポートプリセット(Mobile / Tablet / Full)とシンタックスハイライト付きの折りたたみ可能なソースコードパネルを備えています。すべての MDX ファイルでインポートなしで使用できます。

基本的な使い方

基本的なボックス
<HtmlPreview
  title="基本的なボックス"
  html={`
    <div class="box">Hello, world!</div>
  `}
  css={`
    .box {
      padding: 24px;
      background: #3b82f6;
      color: #fff;
      border-radius: 8px;
      font-family: system-ui, sans-serif;
      text-align: center;
    }
  `}
/>

レスポンシブレイアウト

ビューポートボタン(Mobile / Tablet / Full)を使って、異なる幅でコンテンツがどのように変化するかを確認できます。プレビュー領域の右下にあるドラッグハンドルでリサイズすることもできます。

レスポンシブグリッド

HTML のみ

css プロップを指定しない場合は、HTML のソースのみが表示されます。

HTML のみ

デフォルトでコードを表示

defaultOpen を指定すると、ソースコードが最初から展開された状態で表示されます。

コード表示状態
HTML
<button class="btn">Click me</button>
CSS
.btn {
  padding: 10px 20px;
  background: #8b5cf6;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-family: system-ui, sans-serif;
  cursor: pointer;
}
.btn:hover {
  background: #7c3aed;
}

固定高さ

height プロップを使って、自動調整の代わりに iframe の高さを固定できます。

固定高さ(300px)

高さ100%

fullHeight プロップを使うと、プレビュードキュメントの html/body を iframe いっぱいに広げられます。height: 100% がビューポートまで届くレイアウト(利用可能なスペースを埋めるフレックスカラムなど)に便利です。

明示的な height と併用する

fullHeight は高さ自動調整の仕組みと干渉します — 高さ自動調整は iframe の body を計測して iframe の高さをそれに合わせますが、fullHeight は逆に body の高さを iframe 側から導出させます。両方を組み合わせると、安定した解決策のないフィードバックループが発生します。fullHeight を使うときは必ず明示的な height も指定してください。

高さ100%のフレックスカラム

外部リソース

CSS フレームワーク、Web フォント、スクリプトなどの外部リソースをプレビューに読み込めます。zfb.config.ts でグローバルに設定するか、コンポーネントのプロップで個別に指定します。

グローバル設定

zfb.config.tshtmlPreview を設定すると、すべてのプレビューにリソースが適用されます:

zfb.config.ts
export default defineConfig(
  zudoDoc({
    htmlPreview: {
      head: `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap">`,
      css: `body { font-family: 'Noto Sans JP', sans-serif; }`,
      js: `console.log('preview loaded');`,
    },
  }),
);

コンポーネント単位のプロップ

headjs プロップを使って、個別のプレビューにリソースを追加できます。グローバル設定の後にマージされます。

JS 付き
<HtmlPreview
  title="JS 付き"
  html={`
    <div id="output">待機中...</div>
  `}
  css={`
    #output {
      padding: 16px;
      font-family: system-ui, sans-serif;
      color: #334155;
    }
  `}
  js={`
    document.getElementById('output').textContent = 'JS から Hello!';
  `}
/>

外部スタイルシートとスクリプト

headjs は文字列ベースのプロップで、常に可視の「Head」/「JS」コードブロックとして表示され、プレビューには preflight リセット(すべてのプレビューに注入される Tailwind v4 の CSS リセット)が常に上乗せで適用され、独自リセットを持つフレームワークでもスキップする手段がありません。externalStylesexternalScriptspreflightshowResources は、CDN リソース(CSS フレームワーク、Web フォント、@tailwindcss/browser・Bootstrap・htmx のようなスクリプト)向けに、コードパネルを意識した構造化された代替手段を提供します。

  • externalStyles — スタイルシート URL の配列。<link rel="stylesheet" href="..."> タグとして出力されます。author の css より前に読み込まれるため、css でフレームワークを上書きできます。

  • externalScripts — スクリプト URL の配列。<script src="..."> タグとして出力されます。インラインの js プロップと同じサンドボックス/syncDelay の導出ロジックを通るため、プレビューは自動的に sandbox="allow-scripts allow-same-origin" と 300ms の高さ再同期ディレイを得ます。

  • preflightfalse を指定すると、注入される preflight リセットを完全にスキップします。Tailwind のように独自のベーススタイルを持つフレームワーク向けです。

  • showResources — 両方の配列は(head/js と異なり)デフォルトでは可視のコードパネルから除外されます。読者に読み込まれているリソースを示したい場合は true を指定すると、「HTML」パネルの先頭に <link>/<script src> の生の行として表示されます。

これら4つのプロップはコンポーネント単位のみです — head/css/js と異なり、zfb.config.ts のグローバル htmlPreview 設定には含まれません。

外部リソースはクライアントサイドで読み込まれ、ビルド時には読み込まれない

externalStyles/externalScripts は、プレビューの iframe が描画されるときにブラウザが行うネットワークリクエストであり、ビルド時にバンドルされるアセットではありません。リソースの読み込み中はプレビューが一瞬スタイル未適用の状態で表示されることがあり、そのURLでリソースが利用可能であり続けることに依存します。

@tailwindcss/browser は独自のリセットを持つため preflight={false} を指定する、Tailwind CDN の1行デモ:

Tailwind CDN
<HtmlPreview
  title="Tailwind CDN"
  externalScripts={["https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"]}
  preflight={false}
  html={`
    <div class="flex gap-4 p-6 bg-slate-100">
      <div class="px-4 py-2 bg-blue-500 text-white rounded-lg font-sans">Tailwind</div>
      <div class="px-4 py-2 bg-emerald-500 text-white rounded-lg font-sans">via CDN</div>
    </div>
  `}
/>

showResources を指定すると、CDN の URL が非表示ではなく「HTML」コードパネルの先頭に生の行として表示されます:

Tailwind CDN(リソースを表示)
HTML
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

<div class="px-4 py-2 bg-violet-500 text-white rounded-lg font-sans w-fit">コードを開くとCDNの行が見えます</div>

セキュリティと sandbox プロップ

プレビューは分離された <iframe srcdoc> 内に描画されます。その sandbox 属性は、プレビューにスクリプトが含まれる場合(js プロップ、head 内の <script>、または空でない externalScripts)は allow-scripts allow-same-origin、含まれない場合は allow-same-originデフォルトとします。

信頼の前提

allow-scriptsallow-same-origin を組み合わせると、iframe のサンドボックスは実質的に無効化されます — プレビュー内のスクリプトが親ページのオリジンを共有し、親ドキュメントにアクセスできます。zudo-doc がこのデフォルトを採用しているのは、プレビューの内容が作成者が信頼できる MDX であるためで、また allow-same-origin が高さの自動計測を可能にしているためです。

半信頼またはユーザー投稿の HTML を描画するプロジェクトでは、sandbox プロップでより厳しい値に上書きしてください。

<!-- 最も厳格: スクリプト実行なし、オリジンは不透明 -->
<HtmlPreview html={untrusted} sandbox="" height={400} />

<!-- スクリプトは許可するがオリジンは不透明(親にアクセス不可) -->
<HtmlPreview html={untrusted} sandbox="allow-scripts" height={400} />

allow-same-origin を外すと iframe のオリジンが不透明になり、親から iframe.contentDocument を読み取れなくなります — つまり高さの自動調整が無効になります。厳しい sandbox を指定するときは、必ず固定の height も併用してください。空文字列 "" はそのまま尊重され、プロップを省略したときだけ計算済みのデフォルトにフォールバックします。

Props

プロップデフォルト説明
htmlstring(必須)プレビュー iframe 内に描画する HTML コンテンツ
cssstringundefinedプレビュー iframe 内に適用する CSS スタイル
headstringundefined<head> に挿入する生の HTML(リンク、メタ、フォント)
jsstringundefinedプレビュー iframe 内で実行する JavaScript
titlestringundefinedプレビューヘッダーバーに表示するタイトル
heightnumberautoiframe の固定高さ(ピクセル)。省略時はコンテンツに合わせて自動調整
defaultOpenbooleanfalseソースコードパネルをデフォルトで展開表示する
fullHeightbooleanfalseプレビュードキュメントの html/body を高さ100%まで広げる。高さ自動調整と干渉するため、必ず明示的な height と併用する
sandboxstringautoiframe の sandbox 属性。省略時は計算済みのデフォルト(スクリプトありなら allow-scripts allow-same-origin、なしなら allow-same-origin)。信頼できないコンテンツにはより厳しい値を指定 — ただし allow-same-origin を外すと高さ自動調整が無効になるため height も併せて指定する
externalStylesstring[]undefined外部スタイルシート URL。head/css より前に <link rel="stylesheet"> として挿入される。コンポーネント単位のみ — ビルドバンドルされず、表示時にクライアントサイドで読み込まれる
externalScriptsstring[]undefined外部スクリプト URL。<script src> として挿入される。js と同様にサンドボックス/syncDelay の導出を切り替える。コンポーネント単位のみ — ビルドバンドルされず、表示時にクライアントサイドで読み込まれる
preflightbooleantruefalse を指定すると、注入される preflight リセットをスキップする — externalStyles/externalScripts で読み込む独自リセットを持つフレームワーク向け
showResourcesbooleanfalseexternalStyles/externalScripts を「HTML」コードパネルの先頭に生の行として表示する。デフォルトではパネルから除外される

シンタックスハイライトと遅延 WASM

ソースコードパネルは公開されている @takazudo/zfb-md-wasm のパッケージルートを遅延インポートし、次の言語をセマンティックハイライターで処理します:

  • html — HTML パネルおよび Head パネル

  • css — CSS パネル

  • javascript — JS パネル

レンダラーは安全にエスケープされた pre.hi-root / hi-* マークアップを出力し、ドキュメントフェンスと同じ --zd-syntax-* パレットを共有します。遅延読み込みの境界はパネルを開く操作です。それより前に、生成された JavaScript glue と WASM companion リソースがリクエストされることはありません。未知の言語に対する warning は、エスケープ済みのセマンティックフォールバックマークアップを使用します。インポート、初期化、不正なオプション、現在の呼び出しの失敗時には、JSX でエスケープされたプレーンな <pre><code> フォールバックを維持します。一時的なインポート失敗は、後続のパネル描画で再試行できます。

独自の配信基盤で本番出力を提供する場合は、生成された .mjs / .wasm アセットを保持し、JavaScript と application/wasm の正しい MIME type で配信してください。パッケージ内部の glue パスを手作業でコピーしないでください。リソースグラフは zfb のビルドが管理します。

このブラウザ実行時の言語セットは、通常のビルド時コードブロックで利用できる言語一覧より限定されています。

注意事項

  • プレビューは CSS リセット(Tailwind v4 preflight)を含む分離された <iframe> 内で描画されるため、スタイルが外部に漏れることはありません。

  • プレビューはデフォルトで sandbox="allow-same-origin"js プロップまたは <script> がある場合は sandbox="allow-scripts allow-same-origin")を使用し、contentDocument 経由で iframe の高さを自動同期します。srcdoc の内容は作成者が管理する MDX です。信頼できないコンテンツには sandbox プロップで上書きしてください — 上記のセキュリティと sandbox プロップを参照。

  • htmlPreview 設定フィールドのグローバルリソースは、コンポーネント単位のプロップの前に挿入されます。

  • htmlcssjs プロップはインデント付きのテンプレートリテラルに対応しています。先頭の空白はソースコード表示時に自動的に除去されます。

  • クライアントサイドのハイドレーションはコンポーネントラッパーにより自動的に処理されるため、MDX 内で client:load ディレクティブを付ける必要はありません。

  • externalStyles/externalScripts は表示時にクライアントサイドで読み込まれます — プレビュー iframe が描画されるときにブラウザが行うネットワークリクエストであり、ビルド時にバンドルされるアセットではありません。上記の外部スタイルシートとスクリプトを参照。

  • srcdoc への挿入順序: preflight リセット(preflight={false} でない限り)→ fullHeight スタイル → externalStylesheadcss

Revision History

Takeshi Takatsudo作成: 2026-03-16T16:24:56+09:00更新: 2026-07-25T02:45:54+09:00

AI Assistant

Ask a question about the documentation.

Preview theme

Loading theme previews…