こんにちは、IKKOHです!
ブログ記事でWebフレームワークの解説をするとき、「コードの解説を読むだけじゃなくて、実際に触って動かしてほしい!」と思ったことはありませんか?
静的サイトジェネレーターである Astro は非常に高速ですが、記事内でインタラクティブなコードエディタを動かそうとすると少し工夫が必要です。
そこで今回は、CodeSandboxが提供するオンラインエディタコンポーネント Sandpack を使い、AstroPaperで構築したブログ記事上で Vue.js のサンプルコードをインタラクティブに動作させる方法 を解説します!
途中で発生するAstroPaper特有のビルドエラー対処法も合わせて紹介しますので、ぜひ最後までご覧ください。
🛠️ 今回のゴール
- SandpackとReact統合パッケージをインストールする
- React追加時に発生する
Satoriの型エラーを解消する - Sandpackを呼び出すAstroコンポーネントを作成する
- MDX記事内でVueコンポーネントをインタラクティブに動作させる
ステップ1:Sandpackのインストールと型エラー対処
まずは、以下のコマンドを実行して @codesandbox/sandpack-react をインストールします。
npm install @codesandbox/sandpack-react
SandpackはReactコンポーネントとして提供されているため、AstroプロジェクトにReactサポートを追加します。 Astro公式の自動セットアップコマンドを使用すれば、設定ファイル(astro.config.mjs)への追記も自動で行われます。
npx astro add react
インストール途中に何度か確認プロンプトが表示されますが、すべて Yes で続行して問題ありません。
🚨 ビルドエラーの発生と解決策
インストール完了後、動作確認のためにビルドコマンドを実行してみます。
npm run build
すると、AstroPaperがOGP画像生成のために内部で使用している Satori の呼び出し箇所で、以下のようなTypeScriptの型エラーが発生します。
src/pages/og.png.ts:27:5 - error ts(2345): Argument of type '{ type: string; props: { style: { background: string; width: string; height: string; display: string; alignItems: string; justifyContent: string; fontFamily: string; }; children: ({ type: string; props: { ...; }; } | { ...; })[]; }; }' is not assignable to parameter of type 'ReactNode'.
Type '{ type: string; props: { style: { background: string; width: string; height: string; display: string; alignItems: string; justifyContent: string; fontFamily: string; }; children: ({ type: string; props: { ...; }; } | { ...; })[]; }; }' is missing the following properties from type 'ReactPortal': children, key
27 {
~
28 type: "div",
~~~~~~~~~~~~~~~~~~
...
140 },
~~~~~~~~
141 },
~~~~~
src/pages/posts/[...slug]/index.png.ts:48:5 - error ts(2345): Argument of type '{ type: string; props: { style: { background: string; width: string; height: string; display: string; alignItems: string; justifyContent: string; }; children: ({ type: string; props: { style: { position: string; ... 10 more ...; height: string; }; }; } | { ...; })[]; }; }' is not assignable to parameter of type 'ReactNode'.
Type '{ type: string; props: { style: { background: string; width: string; height: string; display: string; alignItems: string; justifyContent: string; }; children: ({ type: string; props: { style: { position: string; ... 10 more ...; height: string; }; }; } | { ...; })[]; }; }' is missing the following properties from type 'ReactPortal': children, key
48 {
~
49 type: "div",
~~~~~~~~~~~~~~~~~~
...
168 },
~~~~~~~~
169 },
~~~~~
Result (60 files):
- 2 errors
- 0 warnings
- 0 hints
なぜエラーになるの?
Satoriは React環境では ReactNode型を引数に取り、非React環境では 疑似ReactNode型 (ReactNodeから必要な属性のみを抜粋したSatori独自の型) を引数に取ります。 今回、Reactをインストールしたことにより、AstroPaperが引数として渡すべきオブジェクトの型が疑似ReactNode型からReact本来のReactNode型に変わってしまいました。 その結果、引数として渡しているオブジェクト内の属性のみではReactNode型として必須の属性が欠けているため、ビルドエラーとなっています。
Satoriに渡しているオブジェクトに欠けているReactNode型として必須の属性を補完すれば、ビルドエラーを解消できます。
なお、Satoriとしては、もともとAstroPaper側から渡していた属性のみで事足りるため、エラー発生箇所直前に // @ts-ignore を付与して無視する方法でも問題ありません。
26 const svg = await satori(
27 {
28 key: null,
29 type: "div",
30 props: {
31 style: {
32 background: "#fefbfb",src/pages/og.png.ts
47 const svg = await satori(
48 {
49 key: null,
50 type: "div",
51 props: {
52 style: {
53 background: "#fefbfb",src/pages/posts/[...slug]/index.png.ts
これで npm run build が正常に通るようになります!
ステップ2:Sandpackを呼び出すAstroコンポーネントの作成
MDX記事内で毎回複雑なSandpackのオプションを指定するのは大変です。 そこで、記事内から使いやすくするためのラッパーコンポーネントを作成します。
---
// Astroコンポーネント内でSandpackをインポート
import { Sandpack } from "@codesandbox/sandpack-react";
// MDX側から初期コードを文字列として受け取れるようにする
interface Props {
vueCode: string; // 親コンポーネント (/src/App.vue) のコード
editorHeight?: number; // エディタの高さ(デフォルト: 400)
childFiles?: Record<string, string>; // 子コンポーネントのマップ { "Child.vue": "...", "Modal.vue": "..." }
activeFile?: string; // 初期表示するファイル(デフォルト: /src/App.vue)
}
const {
vueCode,
editorHeight = 400,
childFiles = {},
activeFile = "/src/App.vue",
} = Astro.props;
// ファイルリストの構築
const files: Record<string, string> = {
"/src/App.vue": vueCode,
};
// childFiles 内の各ファイルを /src/ 配下にマッピング
Object.entries(childFiles).forEach(([fileName, code]) => {
const path = fileName.startsWith("/src/") ? fileName : `/src/${fileName}`;
files[path] = code;
});
// 表示する全ファイルパス(タブ切り替え用)
const visibleFiles = Object.keys(files);
---
<!--
client:visible をつけることで、画面に表示されたタイミングで
Sandpack(React)をブラウザ側でアクティブにします。
-->
<div class="sandpack-wrapper">
<Sandpack
client:visible
template="vue"
files={files}
options={{
showNavigator: true,
editorHeight: editorHeight,
activeFile: activeFile,
visibleFiles: visibleFiles,
}}
/>
</div>
<style>
/* 必要に応じてエディタの周りの余白を調整 */
.sandpack-wrapper {
margin: 2rem 0;
}
</style>src/components/SandpackVue.astro
ステップ3:MDXでマークダウンを作成する
作成した SandpackVue コンポーネントをMDXファイルで読み込み、Vueのサンプルコードを渡します。 MDXでは通常のマークダウン記法に加えて、Astroコンポーネントを呼び出すことができます。
---
<!-- 通常のマークダウンと同様にフロントマターを記述します -->
author: IKKOH
pubDatetime: 2026-07-24T00:00:00+09:00
title: 記事上でVue.jsを動作させる
・・・
---
import SandpackVue from '@/components/SandpackVue.astro';
以下のエディタでは、実際にVue.jsのコードを編集して動作を確認できます!
<SandpackVue vueCode={`
<template>
<div class="counter">
<h1>Hello Vue in Astro!</h1>
<p>カウント: {{ count }}</p>
<button @click="count++">カウントアップ</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<style scoped>
.counter {
font-family: sans-serif;
text-align: center;
padding: 20px;
}
button {
padding: 8px 16px;
cursor: pointer;
}
</style>
`}
/>
ステップ4:プレビューモードで動作確認
準備が整ったら、ビルドしてプレビューモードで動作を確認しましょう!
npm run build
npm run preview
ブラウザで記事を開くと、以下のように実際にブラウザ上で編集・実行可能なVue.jsのエディタが表示されるはずです🎉
開発サーバーでの動作確認について
SandpackはiFrame内でのWebContainer連携やViteのSSRモジュール解決において、開発モード(dev)環境と干渉を起こす場合があります。
開発時に挙動がおかしいと感じた場合は、一度 npm run build & npm run preview を実行してプロダクションビルド状態での確認をおすすめします。
まとめ
今回は、AstroPaperで構築したブログサイトにSandpackを導入し、記事上でVue.jsのサンプルコードを動かす環境を整えました。
npx astro add reactで手軽にReact環境を統合- Satoriの型エラー は
key: nullの追加で解決 client:visibleで必要な時だけエディタをインタラクティブ化
静的ブログでありながら、読者がその場でコードを書き換えて実験できる体験を提供できるのはSandpackの大きな魅力です!
次回は、今回構築した環境を使って Vue.jsの基本概念(リアクティブシステムやコンポーネント設計) について分かりやすく解説していきます。お楽しみに!