カスタマイズ
コンポーネントの上書き、インタラクティブなアイランドの追加、カスタムページのマウント、レジストリコンポーネントのインストール、または完全な制御が必要な場合の完全なイジェクト。
コンポーネントの上書き
プロジェクトルートに components.ts(または components.tsx)を追加し、defineComponents をエクスポートします。mdx マップは組み込みコンポーネントを置き換えるか、新しいコンポーネントを追加します — インポートなしですべての .mdx ページで利用できます。
import { defineComponents } from "blume";
import Callout from "./components/Callout.astro";
import Pricing from "./components/Pricing.astro";
export default defineComponents({
mdx: {
Callout, // replace the built-in Callout
Pricing, // add a new <Pricing /> component
},
});
キーは MDX 内で記述する名前です(<Callout>、<Pricing>)。React コンポーネントをインポートする場合は .tsx のファイル名を使用してください。
組み込みコンポーネント、アイランド、上書きのいずれでもない大文字始まりのタグがあると、レンダリング時にそのページが失敗します。そのため、blume dev、blume build、blume check は事前にそれを BLUME_UNKNOWN_COMPONENT として警告します。警告はタグごとに 1 回出力され、そのタグを使用しているすべてのページが示されます。
参照の形式
mdx と layout のいずれにおいても、すべての上書きは 3 つの形式を受け付けます。
import { defineComponents } from "blume";
import Callout from "./components/Callout.astro";
export default defineComponents({
mdx: {
Callout, // 1. an imported component
Note: "./components/Note.astro", // 2. a path string (resolved from the project root)
Chart: { component: "./components/Chart.tsx", client: "load" }, // 3. a descriptor
},
});
Blume は components.ts を静的に読み取ります — ファイルを実行することはありません — そのため、受け付けるのはこれら 3 つの形式のみです。インライン関数や式、ファイル内で宣言されたコンポーネント、スプレッド、計算されたキー、または以下のモード文字列のいずれでもない client は、該当するエントリ名を示す BLUME_COMPONENTS_INVALID エラーになります。blume dev はこのエラーをターミナルとブラウザのオーバーレイに表示し、blume build は失敗します。
ディスクリプタ形式ではハイドレーションモードを追加できるため、インタラクティブな React/Vue/Svelte コンポーネントが JavaScript を配信し、クライアント側で動作するようになります。client モードがない場合、フレームワークコンポーネントは静的な HTML としてレンダリングされます。これは通常ミスであるため、Blume はそうしたケースを検出するとビルド警告を出力します。
client |
ハイドレーションのタイミング |
|---|---|
"load" |
ページ読み込み時に即座に |
"idle" |
メインスレッドがアイドル状態になったとき |
"visible" |
スクロールして表示領域に入ったとき |
"media" |
media クエリが一致したとき(media: "(min-width: 40rem)" を追加します) |
"only" |
クライアントのみ、サーバーレンダリングは行われません |
client モードを持つ mdx エントリは、それ自体がアイランドです。すべてのページで利用でき、islands/ フォルダー に配置したコンポーネントとまったく同じようにハイドレートされます。フォルダーは引き続き設定不要の方法です — ファイル名とは異なる名前を使いたい場合、media クエリを使いたい場合、または他の上書きと並べてアイランドを管理したい場合に components.ts のエントリを使用してください。
上書きの型付け
組み込みコンポーネントを置き換える際は、blume/components からそのプロップ型をインポートして、コンポーネントが契約に適合するようにしてください。型はコンポーネント自体から導出されるため、乖離することはありません。
import type { CalloutProps } from "blume/components";
export default function Callout(props: CalloutProps) {
// …your own callout, same props as the built-in
}
プロップ型はコンテンツコンポーネント向けにエクスポートされています(CalloutProps、CardProps、TabsProps、StepsProps、BadgeProps など)。
レイアウトスロット
layout マップは Blume のクロム(外枠 UI)の一部を独自のコンポーネントに置き換えます。各上書きは置き換え対象の組み込みコンポーネントと同じプロップを受け取るため、デフォルトをラップすることも、ゼロから作ることもできます。
import { defineComponents } from "blume";
import Footer from "./components/Footer.astro";
import Logo from "./components/Logo.astro";
export default defineComponents({
layout: {
Logo, // brand mark + title in the header
Footer, // site-wide footer, in place of the one `footer` configures
},
});
配線済みのスロット(名前は大文字と小文字が区別されます。layout 配下のそれ以外のキーはどこにもレンダリングされず、Blume が警告を出します):
| スロット | 置き換え対象 | プロップ |
|---|---|---|
Layout |
ページシェル全体(RootLayout) |
組み込みレイアウトが受け取るすべて、加えて layout マップ |
Header |
上部のナビゲーションバー | site、logo、navigation、route、searchEnabled など |
Logo |
ヘッダー内のブランドリンク(マーク+タイトル) | site、logo、locale |
Search |
ヘッダーの検索トリガー+モーダル | navigation、strings、locale、assistantEnabled |
Sidebar |
主要なナビゲーションツリー | items、currentRoute |
MobileNav |
モバイルドロワー内のナビゲーション(デフォルトは Sidebar) |
items、currentRoute |
Breadcrumbs |
パンくずリスト | crumbs |
TableOfContents |
右側のレール(variant: "desktop")と、狭い画面ではコンテンツ上部のドロップダウン("mobile")に表示される、このページの目次 |
headings、title、variant |
Pagination |
前後のページへのフッターリンク | prev、next、strings |
Feedback |
記事の下にある「このページは役に立ちましたか?」の評価(feedback が有効な場合のみレンダリングされます) |
strings、comments |
PageHeader |
記事の上部への挿入ポイント(組み込みなし) | page、headings、route |
PageFooter |
記事の下部への挿入ポイント(組み込みなし) | page、headings、route |
Footer |
コンテンツグリッドの後に配置されるサイトフッター(リンク、ソーシャルプロフィール、リポジトリへのリンク、Cookie 設定) | footer、locale、site、navigation、ui |
mobile バリアントは ページアクション も子要素として受け取ります(幅 1,280px 未満で右側のレールからアクションが移動してくる空の要素です)。そのため、狭い画面でもアクションを表示し続けるには、その中で <slot /> をレンダリングしてください。見出しのないページでも headings が空の状態でレンダリングされるため、そうしたページでもアクションは表示されます。
PageHeader と PageFooter には組み込みコンポーネントがありません。設定するまで何もレンダリングされないため、プロモーションバナーや「最終更新日」の注記などの便利な挿入ポイントになります。PageFooter は、API オペレーションページを含め、Blume がコンテンツからビルドするすべてのページの末尾にレンダリングされ、その後に Feedback と Pagination が続きます。custom または frame モードのページでは、タイトルやパンくずリストとともにこのページ末尾の領域が省略されます。pages/ 配下のカスタムページでは、そのページが自身をラップしているレイアウトがレンダリングされます。RootLayout は layout プロップを通じてスロットの上書きを受け取り、PageLayout にはページ末尾の領域がありません。組み込みの Footer はリポジトリとソーシャルプロフィールへの唯一のリンクを保持しているため、Footer の上書き(設定では表現できないマーケティング用フッターを置く場所)でもそれらを残してください。consent を設定している場合は、それを再び開く手段も残してください。data-blume-consent-open を持つ任意の要素が Cookie 設定 のリンクとして機能します。
レイアウトスロットは MDX の上書きと同じ3 つの参照形式を受け付けます。そのため、インタラクティブなヘッダーやフッターが必要な場合は、スロットにパス文字列やハイドレーション付きのディスクリプタ({ component, client })を指定できます。
インタラクティブなアイランド
インタラクティブな UI(React、Vue、Svelte)については、コンポーネントを islands/ フォルダーに配置すれば、任意の MDX ページで使用できます。Blume が自動的にハイドレートするため、ラッパーや登録は不要です。
import { useState } from "react";
export default function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>Clicked {n}</button>;
}
Use it anywhere: <Counter />
ハイドレーション戦略とフレームワークのセットアップについては アイランド を参照してください。
カスタムページ
pages/ フォルダー配下に .astro ファイルを追加すると、ドキュメントと並んで完全なカスタムルートをマウントできます — ランディングページ、価格ページ、手作りのインデックスなどです。これらは配置場所を維持するため、相対インポートや getStaticPaths は通常どおり機能し、blume:data モジュールから設定、ナビゲーション、ルートを読み取ることができます。
完全なガイドは カスタムページ を参照してください。
レジストリ
blume add は Blume がメンテナンスするコンポーネントをソースとしてプロジェクトにコピーします — そのコードはあなたのものであり、自由に編集できます。引数なしで実行すると、利用可能なものが一覧表示されます。
blume add
レイアウトスロット(ヘッダー、サイドバー、パンくずリスト、目次、ページネーション、フィードバック)や任意のコンテンツコンポーネント(コールアウト、カード、タブ、ステップ、アコーディオンなど)をインストールできます。
blume add callout
blume add pagination
コピーされたコンポーネントはフレームワークの残りの部分を blume/* からインポートするため、変更を加えるまでは組み込みのものとまったく同じようにレンダリングされます。blume add は登録用の defineComponents スニペットを出力します — コンテンツコンポーネントは mdx 配下、レイアウト部品は layout 配下です。
Astro インテグレーション
blume.config.ts のトップレベルの integrations 配列から、任意の Astro インテグレーションを追加できます。まずサイトにインテグレーションをインストールしてください。Blume は生成されるランタイムの依存関係にそれを追加せず、Astro との互換性も管理しません。
npm install @astrojs/partytown
import partytown from "@astrojs/partytown";
import { defineConfig } from "blume";
export default defineConfig({
integrations: [
partytown({
config: { forward: ["dataLayer.push"] },
}),
],
});
Blume は組み込みのインテグレーションを既存の順序のまま保持し、その後に宣言順であなたのエントリを追加します。並べ替えや重複排除は行わないため、同じ name を持つ 2 つのインテグレーションは両方とも実行されます。Blume は integrations が配列であることを検証し、各エントリの検証と無効なインテグレーションの報告は Astro が行います。
Blume はインスタンスをコピーするのではなく、生成された Astro 設定から blume.config.ts を再インポートすることでインテグレーションを読み込むため、設定モジュールは 1 回の実行につき 2 回評価されます — Blume が設定を読み込むときと、Astro がそれを読み込むときです。2 回目の評価が無害であるように、インテグレーションのファクトリは副作用のない状態に保ってください(インテグレーションを返すだけにし、構築時にファイルを書き込んだり接続を開いたりしないこと)。
同じインテグレーションが blume dev と blume build の両方で実行されます。blume dev の実行中に blume.config.ts を編集すると、隠れた Astro 設定が再生成され、設定の再起動がトリガーされます。編集したインテグレーションが反映されない場合は、blume dev を再起動してください。Blume はどの設定編集がインテグレーションに影響するかを判別できないため、integrations が空でなくなると、blume.config.ts へのすべての編集は — 無関係なフィールドへの編集であっても — ホットに適用されるのではなく開発サーバーを再起動します。Blume は blume.config.ts の内容のみを追跡するため、そこからインポートされる別ファイルを編集しても、それだけでは再生成はトリガーされません。そのような編集の後は blume dev を再起動してください。イジェクトした場合、所有することになる astro.config.mjs が blume.config.ts への相対的なブリッジを保持するため、設定されたインテグレーションは引き続き実行されます。完全な所有権を引き受ける一環として、後から Astro 設定へ直接移動することもできます。
イジェクト
完全な制御が必要な場合は、生成されたランタイムを独立した Astro プロジェクトへイジェクトできます。
npx blume eject --yes
イジェクトは一方向のステップです。隠れた .blume/ ランタイムが、あなたが所有し直接変更できる通常の Astro アプリになります。blume パッケージは引き続きインポート可能なので、そのコンポーネント、テーマ、Markdown プロセッサはそのまま使えます。
イジェクトは package.json のスクリプトを astro dev と astro build に向け、Astro アプリが名前でインポートするパッケージ — astro、@tailwindcss/vite、生成されるスタイルシート用の tailwindcss と @tailwindcss/typography、設定が組み込む検索クライアント・インテグレーション・アダプター、アイランド・サンプル・アシスタントが使用する場合の React、アシスタントルート用の ai、EPUB エクスポート用の epub-gen-memory — を、Blume 自身が使用しているバージョン範囲で追加します。dev や build の前にインストールを実行してください。追加されたパッケージはイジェクト時に一覧表示されます。イジェクトされたアプリ内のパスはすべて相対パスであるため、CI を含むどのチェックアウトからでもビルドできます。
それ以降は、アプリ独自のスクリプト(npm run dev、npm run build)で実行してください。イジェクトされたプロジェクトでは blume dev、blume build、blume check、blume sync、blume preview は停止し、それらのスクリプトを使うよう案内します。blume eject を再度実行した場合も、編集内容を上書きしてしまうため拒否されます。それでもアプリを再生成したい場合は --force を指定してください。
イジェクトで維持されるもの
隠れたランタイムがページとして提供していたルートはアプリの src/pages に書き出されるため、同じ URL に応答します: 各ページに対応する Markdown 版、/404.md と /404.json の対応版を備えた 404 ページ、ホスト型 MCP サーバー、そして llms.txt と Not Found ページがエージェントに案内する /api/docs/… および /openapi.json の JSON ドキュメント API です。
イジェクトされたアプリの build スクリプトは素の astro build を実行しますが、blume build が上に重ねていた成果物 — 検索インデックス(およびホスト型プロバイダーのインデックス同期)、llms.txt と llms-full.txt、sitemap.xml、robots.txt、agent-readability.json、.well-known ディスカバリーファイル、Agent Skills、プラットフォームの _redirects/_headers ファイル — は引き続き生成されます。イジェクトされた astro.config.mjs 内の Blume インテグレーションが、CLI と同じようにプロジェクト(blume.config.ts とコンテンツ)をスキャンし、Astro の astro:build:done フックからそれらを書き出します。イジェクトされたビルドで行われないのは、CLI のアダプター後処理です: Vercel と Cloudflare の Accept: text/markdown ルーティングの差し込み、Vercel の関数バンドル監査、node() のサーバーエントリラッパー(.well-known ディスカバリーファイルのメディアタイプと CORS ヘッダー、ダウンロードされる SVG のサンドボックス、各リダイレクトの正確なステータス)、netlify() のサーバービルドが .netlify/v1/config.json に書き込むヘッダールール、Cloudflare の Worker の命名(プロジェクト名にちなむ)とプロジェクトルートから wrangler deploy を実行するための .wrangler/deploy リダイレクト、そして --analyze/--budget-* ゲートです。