カスタムコンポーネント
表示用 MDX コンポーネント、オーバーライド、実験的なインタラクティブアイランドを追加する。
コンポーネントを追加する2つの方法
コンポーネントを MDX から利用可能にする方法は2つあります。
1ファイルでインポートする: 1つのドキュメント、または明示的な少数のドキュメントだけで使う場合に向いています。
mdxExtrasでグローバル登録する: ドキュメント全体でコンポーネントを使えるようにする場合、または組み込みの MDX コンポーネントを置き換える場合に向いています。
どちらもサーバーでレンダリングされます。共有のオーサリング面が必要なときだけグローバル登録を選んでください。通常のドキュメント専用 UI には必須ではありません。
ファイル単位のインポート
.mdx ファイルの先頭で直接コンポーネントをインポートします。scaffold の @/ エイリアスは MDX からも使えるため、他のプロジェクトコードと同じ場所にコンポーネントを置けます。
import { MyBadge } from "@/components/my-badge";
<MyBadge>New</MyBadge>これは最小で最もローカルな選択肢です。素の MDX インポートは SSR 専用です。コンポーネントを HTML としてレンダリングしますが、クライアントコンポーネントをハイドレートしません。ブラウザー側の状態やイベントハンドラーが必要な場合は、インタラクティブアイランド(実験的)を参照してください。
グローバル登録: mdxExtras
mdxExtras はプロジェクト所有の MDX コンポーネントのグローバルレジストリーです。bindings モジュールを作成し、zudoDoc() からそのモジュールを指定します。新しい scaffold のルートは設定済みモジュールを virtual:zudo-doc-chrome-bindings 経由ですでに消費するため、表示用コンポーネントではルートスタブを編集しません。
まず、次の完全な例から始めます。
import type { ComponentChildren } from "preact";
export function MyBadge({ children }: { children: ComponentChildren }) {
return (
<span className="rounded-full bg-surface px-hsp-sm py-vsp-2xs text-caption text-fg">
{children}
</span>
);
}import { defineChromeBindings } from "@takazudo/zudo-doc/chrome-bindings";
import { MyBadge } from "./components/my-badge";
export const chromeBindings = defineChromeBindings({
mdxExtras: { MyBadge },
});import { defineConfig } from "zfb/config";
import { zudoDoc } from "@takazudo/zudo-doc/config";
export default defineConfig(
zudoDoc({
siteName: "My Docs",
chromeBindingsModule: "./src/chrome-bindings.tsx",
}),
);生成された pages/docs/[[...slug]].tsx とロケール版は、どちらも virtual:zudo-doc-chrome-bindings から chromeBindings をインポートし、createChrome(routeCtx, chromeBindings) を呼びます。doc history が有効な場合も、generator は同じオブジェクトを置換せず、その上へ DocHistory をマージします。これは表示専用でサーバーレンダリングされる mdxExtras と他のすべてのホスト binding に対する、ルートを fork しないサポート済み経路です。
仮想パスではアイランドをハイドレートできない
生成された仮想経路は、bindings モジュール内で宣言したコンポーネントをスキャナーから到達可能にはしません。そのため、表示用 binding をハイドレートするクライアントアイランドへ自動変換するものではありません。実験的アイランドがルートレベルの静的到達性を必要とする場合だけ、後述のとおり該当するすべてのルートスタブからプロジェクト bindings モジュールを明示的にインポートします。通常の mdxExtras には不要な高度なルートカスタマイズです。
登録後は、すべての MDX ページでインポートなしにコンポーネントを使えます。
<MyBadge>New</MyBadge>配信チャネルと他のホストバインドスロットについては、ホストクロームバインディングを参照してください。
組み込みコンポーネントのオーバーライド
mdxExtras は、組み込みコンポーネントと同じキーを使うと、そのコンポーネントをオーバーライドします。解決は2段階です。chrome/derive がホストの mdxExtras をパッケージデフォルトの上にマージし、その後パッケージの MDX コンポーネントファクトリーがデフォルトマップの後で extras を最後にスプレッドします。そのため、同名の bindings が優先されます。
オーバーライド可能な名前には次が含まれます。
アドモニション:
Note、Tip、Info、Warning、Danger、Important、Cautionコンテンツヘルパー:
Details、HtmlPreview、CodeGroup、Tabs、TabItem、Island、PresetGenerator、Asset、AssetCodep、a、img、tableなどの HTML オーバーライド
置き換えるコンポーネントは、既存ドキュメントが使う MDX 構文と props に互換性を持たせてください。p などの HTML 要素を置き換えると、ドキュメント内の該当するすべての要素に影響します。グローバルなレンダリング変更が意図的な場合を除き、新しい名前付きコンポーネントを優先してください。
Asset と AssetCode は、アセットマニフェストに基づくカードとソース抜粋を表示するパッケージ標準コンポーネントです。記述方法とマニフェストを生成する設定は アセットビューアーを参照してください。
class ではなく className
プロジェクトの .tsx コンポーネントでは class ではなく className を書きます。scaffold は react を preact/compat にマッピングするため、作成したコンポーネントは React 互換 JSX 型を通して型チェックされます。class= は zfb check で TS2322 となり、className は scaffold と Preact 互換コードの両方で動きます。
// Correct in project-owned .tsx files.
export function MyBadge() {
return <span className="text-caption">New</span>;
}zudo-doc のパッケージやショーケースのソースには class= を使うものがあります。それらは独自の Preact コンパイル境界に従っています。その綴りを新しく作成する scaffold コンポーネントへコピーしないでください。
インタラクティブアイランド(実験的)
インタラクティブなコンポーネントには MDX インポート以上のものが必要です。モジュールでクライアント実行を明示的に有効にし、zfb が静的ルートグラフを通じてそのインポートへ到達できなければなりません。次のレシピは、同じ静的 bindings 経路から、画面に現れたときに動くカウンターアイランドを登録します。
"use client";
import { useState } from "preact/hooks";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button type="button" className="rounded-full bg-surface px-hsp-sm py-vsp-2xs text-caption text-fg" onClick={() => setCount((value) => value + 1)}>
Count: {count}
</button>
);
}import { Island } from "@takazudo/zfb";
import { defineChromeBindings } from "@takazudo/zudo-doc/chrome-bindings";
import Counter from "./components/counter";
(Counter as { displayName?: string }).displayName = "Counter";
const CounterIsland = () =>
Island({
when: "visible",
children: <Counter />,
});
export const chromeBindings = defineChromeBindings({
mdxExtras: { CounterIsland },
});この実験的アイランドの場合だけ、該当するすべてのドキュメントルートスタブで、生成済みの仮想インポートを静的な . インポートへ置き換えます(ロケールルートでは相対パスを調整します)。デフォルトの仮想モジュール経路は SSR 表示専用であり、このアイランドが検出されることを保証できません。Counter.displayName を設定すると、レンダリングパイプラインが関数名を書き換える場合にもアイランドマーカー名を固定できます。
実験的なスキャナー契約
アップストリームのアイランドマーカー重複排除は依然として壊れやすいため、このパターンは実験的です。その作業は issue #2718 を追跡してください。アイランドの登録経路を変更したときは、必ずビルド済みサイトでハイドレート後の動作を確認してください。
このショーケースの pages/ は、同じ Island({...}) 呼び出し形式と display-name の固定を示しています。表示専用の className ベースコンポーネントについては、このショーケースの src/ にある Pill レンダラーを参照してください。
登録したアイランドは、他のグローバルコンポーネントと同様に MDX から使えます。
<CounterIsland />関連項目
最小から拡張までのカスタマイズのはしごは zudo-docのカスタマイズ
bindings モジュールとコンポーネント以外のスロットは ホストクロームバインディング
通常の MDX オーサリングは ドキュメントを書く