Skip to content

MDX ブログポストにインタラクティブなコードプレイグラウンドを導入する #1573

Description

@ryota-murakami

概要

react.dev のように、ブログ記事内でインタラクティブに動作するUIコンポーネントとコードエディタを表示したい。現在の laststance.io は Next.js 16 + @next/mdx + remark-gfm + @mapbox/rehype-prism のスタックで MDX ベースのブログを運用しているため、この既存構成に統合可能なツールを調査した。


現行スタック

レイヤー パッケージ
フレームワーク Next.js 16 (App Router)
MDX @next/mdx + @mdx-js/loader
Remark プラグイン remark-gfm
シンタックスハイライト @mapbox/rehype-prism
MDX コンポーネント mdx-components.tsx(最小パススルー)

Option A: Sandpack(推奨)⭐

react.dev 公式サイトで採用されている、CodeSandbox チームのインブラウザバンドラー

特徴

  • フルバンドラー: ブラウザ内で npm パッケージの import が動作
  • HMR 対応: コード編集時にリアルタイムプレビュー更新
  • iframe 分離: ユーザーコードがホストページに影響しない
  • テンプレート: React, Vue, Vanilla JS, TypeScript 等のプリセット
  • テーマ: 多数のビルトインテーマ + カスタムテーマ対応
  • Josh Comeau, Liveblocks 等の著名プロダクトで実績あり
  • ⚠️ バンドルサイズ大(sandpack-client 含めて gzipped ~400KB+)
  • ⚠️ remark-sandpack プラグインは 2025年3月にアーカイブ済み(後述の代替方法を推奨)

インストール

pnpm add @codesandbox/sandpack-react

MDX への統合方法(2通り)

方法 1: カスタム MDX コンポーネントとして直接使用(推奨)

mdx-components.tsxSandpack を登録して MDX から直接使う。remark プラグイン不要。

// mdx-components.tsx
import { Sandpack } from '@codesandbox/sandpack-react'
import type { MDXComponents } from 'mdx/types'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
    Sandpack, // MDX から <Sandpack /> として使える
  }
}
<!-- articles/my-post/page.mdx -->

# React Hooks の使い方

以下のコードを自由に編集してみてください:

<Sandpack
  template="react"
  files={{
    "/App.js": `import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  )
}`
  }}
  options={{
    showLineNumbers: true,
    editorHeight: 300,
  }}
/>

方法 2: remark-sandpack プラグイン⚠️ 非推奨 — アーカイブ済み)

remark-sandpack は 2025年3月にアーカイブされたため、新規導入は推奨しない。方法 1 のカスタムコンポーネント方式を使用すること。

参考リンク


Option B: react-live

シンプルなインラインプレビュー。Bublé で JSX をブラウザ内トランスパイル

特徴

  • 軽量: Bublé tree-shaken 版で ~83KB gzipped
  • ✅ シンプルな API(LiveProvider, LiveEditor, LivePreview, LiveError
  • ✅ SSR 対応
  • ⚠️ npm パッケージの import不可(スコープ内のコンポーネントを明示的に渡す必要あり)
  • ⚠️ Bublé は ESNext の一部機能未対応(Optional Chaining 等)
  • ❌ HMR なし(編集のたびに再トランスパイル)

インストール

pnpm add react-live

MDX への統合方法

// mdx-components.tsx
import { LiveProvider, LiveEditor, LivePreview, LiveError } from 'react-live'
import type { MDXComponents } from 'mdx/types'

function InteractiveCode({ code, scope = {} }: { code: string; scope?: Record<string, unknown> }) {
  return (
    <LiveProvider code={code} scope={scope}>
      <LiveEditor />
      <LivePreview />
      <LiveError />
    </LiveProvider>
  )
}

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
    InteractiveCode,
  }
}
<!-- articles/my-post/page.mdx -->

# インラインプレビューのデモ

<InteractiveCode
  code={`function Hello() {
  return <h1>Hello, World!</h1>
}

render(<Hello />)`}
/>

参考リンク


Option C: Code Hike v1

MDX 特化の remark プラグイン。コードブロックにアノテーション、スクローリテリング、ツールチップを追加

特徴

  • ✅ MDX とのネイティブ統合(remark プラグイン)
  • ✅ コードブロックのアノテーション(行ハイライト、ツールチップ、diff 表示)
  • ✅ スクローリテリング(スクロールに連動してコードをステップ表示)
  • ✅ Fine-grained Markdown(MDX コンテンツを小さなパーツに分解して React で自由にレンダリング)
  • ✅ 軽量(ランタイムバンドラーを含まない)
  • ⚠️ ライブコード編集は不可(静的表示 + インタラクティブアノテーション)
  • ⚠️ Astro 非対応

インストール

pnpm add codehike

MDX への統合方法

// next.config.mjs
import { remarkCodeHike, recmaCodeHike } from 'codehike/mdx'

const chConfig = {
  components: { code: 'Code' },
}

const nextConfig = {
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
}

const withMDX = nextMDX({
  options: {
    remarkPlugins: [[remarkCodeHike, chConfig]],
    recmaPlugins: [[recmaCodeHike, chConfig]],
  },
})

export default withMDX(nextConfig)
<!-- articles/my-post/page.mdx -->

# TypeScript の型システム

```ts
// !hover[/useState/]
const [count, setCount] = useState(0)
// ^^^ React の組み込みフック。状態管理に使用。

useState にホバーすると説明がツールチップで表示される


### 参考リンク

- [Code Hike v1 公式](https://codehike.org/docs)
- [Code Hike v1 リリースブログ](https://codehike.org/blog/v1)
- [GitHub — code-hike/codehike](https://github.com/code-hike/codehike)

---

## Option D: カスタム MDX コンポーネント(ライブラリ不要)

> **最も柔軟で最も手間がかかる方法**。MDX に任意の React コンポーネントを埋め込む

### 特徴

- ✅ 外部依存なし — バンドルサイズ増加ゼロ
- ✅ 任意の React コンポーネントを MDX に埋め込める
- ✅ 完全なデザインコントロール
- ⚠️ コードエディタ機能は自前実装が必要
- ⚠️ コンポーネントごとの開発コスト大

### MDX への統合方法

```tsx
// components/demos/CounterDemo.tsx
'use client'
import { useState } from 'react'

export function CounterDemo() {
  const [count, setCount] = useState(0)
  return (
    <div className="rounded-lg border p-4">
      <button onClick={() => setCount(c => c + 1)}>
        Count: {count}
      </button>
    </div>
  )
}
// mdx-components.tsx
import { CounterDemo } from '@/components/demos/CounterDemo'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
    CounterDemo,
  }
}
# デモ

<CounterDemo />

比較表

項目 Sandpack ⭐ react-live Code Hike v1 カスタムコンポーネント
ライブコード編集 ❌ 静的表示 ❌(自前実装なら可)
npm import N/A N/A
バンドルサイズ ~400KB+ gzip ~83KB gzip 軽量(静的) 0KB(追加なし)
MDX 統合 コンポーネント登録 コンポーネント登録 remark プラグイン コンポーネント登録
セットアップ難度 高(コンポーネントごと)
採用実績 react.dev, Liveblocks Formidable Labs Next.js docs 系
react.dev と同等 ✅ 同じツール
メンテナンス状態 活発 安定 活発 (v1)

推奨

「react.dev と同じ体験」を目指すなら → Sandpack (Option A)

  • react.dev がまさに Sandpack で構築されている
  • npm パッケージの import が動くため、実用的なコード例を示せる
  • mdx-components.tsx にコンポーネント登録するだけで MDX から使える
  • バンドルサイズは大きいが、ブログ記事ページのみで読み込めば影響は限定的

「コードアノテーション・教育コンテンツ」なら → Code Hike v1 (Option C)

  • ライブ編集は不要だが、コードの特定箇所にツールチップや段階的表示をしたい場合に最適
  • 技術記事やチュートリアルの「読み進める」体験に向いている

「軽量かつシンプル」なら → react-live (Option B)

  • 小さなコード例のインラインプレビューに適している
  • npm import が不要な単純なデモであれば十分

ハイブリッドアプローチも可能

Sandpack(重い記事用) + Code Hike(コード解説用) + カスタムコンポーネント(個別デモ用)を併用することも可能。MDX はコンポーネントベースなので、記事ごとに最適なツールを選べる。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions