Skip to content
戻る

【Astro】ブログ記事内でVue.jsを動かす!Sandpack導入手順とSatoriエラーの対処法

更新日:

こんにちは、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
Tip

インストール途中に何度か確認プロンプトが表示されますが、すべて 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のエディタが表示されるはずです🎉

<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>

開発サーバーでの動作確認について

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の基本概念(リアクティブシステムやコンポーネント設計) について分かりやすく解説していきます。お楽しみに!