Next.js アプリのコンポーネント群をアプリ外の単体 React バンドルへ切り出す定石 — シム・コンテキスト同一性・JIT CSS
ライブラリ dist を持たない Next.js アプリから、コンポーネントカタログや外部ツール向けに一部コンポーネントを esbuild 等で単体 IIFE バンドル化する場合、アプリランタイムが暗黙に供給しているものを列挙して埋める必要がある。詰まりどころは4系統に整理できる。
1. process シム
Next.js のコードはビルド時 define を前提に process.env を参照するため、ブラウザ単体では process is not defined で即死する。globalThis に最小の process(env に NODE_ENV 等)を生やすモジュールを、バンドルのエントリ結合の先頭に置いて本体より先に評価させる。
2. App Router コンテキストのモック
useRouter / usePathname / useSearchParams / useParams は Next 内部の共有コンテキスト(app-router-context / hooks-client-context の shared-runtime モジュール)を読む。これらの Provider に no-op のモックルーターや固定値を渡すラッパーを作り、プレビューの外殻として噛ませる。router 不在の症状は invariant expected app router to be mounted。
3. コンテキスト同一性(最重要・最も気づきにくい)
Provider コンポーネント(i18n、react-query、状態管理)とそれを消費するコンポーネントが別々のバンドル(本体バンドルとテスト側コンパイル)から来ると、同じライブラリでもモジュールインスタンスが別になり React コンテキストが繋がらず、missing-provider エラーになる。Provider は必ず本体バンドルのエクスポートとして公開し、消費側と同一バンドル由来に揃える。「Provider を正しく巻いているのに context が無いと言われる」症状はほぼこれ。
4. Tailwind JIT CSS とフォント
Tailwind のユーティリティは JS バンドルに含まれず、アプリのビルドパイプラインでしか生成されない。Tailwind CLI で対象ソースを content に指定して安定パスへコンパイルし、成果物 CSS として同梱する。切り出し先で新規マークアップを書く用途があるなら、使用実績のないブランド系ユーティリティは safelist で確保する。public 配下の絶対パス参照(フォント・画像)は切り出し先で 404 になるため、相対パス版 font-face の用意や data URI 化で置き換える。
検証
headless ブラウザで各コンポーネントを実レンダリングし、コンソールエラーゼロ・root 非空・スタイル適用(フォールバックフォントでないこと)を機械チェックする。フォントは両環境が同じフォールバックで見かけ上一致してしまうため、目視比較では検出できない点に注意する。