AstroのSSG環境でChart.jsを使おうとすると、ビルド時にインポートエラーが出る——これはクライアントサイドライブラリをサーバーサイドでビルドしようとするSSG特有の問題です。
本記事では、ECサイトの商品詳細ページで評価チャート(レーダーチャート)を実装した際に遭遇した複数の問題と、その解決策を記録します。
完成イメージ
商品の特徴を6つの軸で可視化するレーダーチャートです。
- チャート項目(上から右回り): 品質・耐久性・デザイン・コスパ・機能性・サポート
- スコア: 0〜5の6段階
- 用途: 商品詳細ページで、各商品の評価プロファイルを直感的に表示
問題1: Failed to resolve import "chart.js"
発生状況
AstroでChart.jsをインポートしてビルドすると、以下のエラーが複数回発生しました。
1Failed to resolve import "chart.js"このエラーは開発中に繰り返し遭遇し、その都度対処が必要でした。
原因
AstroのSSGビルドはサーバーサイド(Node.js環境)で実行されます。Chart.jsはブラウザの <canvas> APIに依存するクライアントサイドライブラリのため、サーバーサイドでのインポートに失敗します。
解決策:クライアントサイドスクリプトで読み込む
Chart.jsのインポートと初期化は、Astroの <script> タグ内(クライアントサイド)で行います。
1---2interface Props {3 labels: string[];4 data: number[];5 maxValue: number;6}7
8const { labels, data, maxValue } = Astro.props;9---10
11<canvas12 id="radar-chart"13 data-labels={JSON.stringify(labels)}14 data-values={JSON.stringify(data)}55 collapsed lines
15 data-max={maxValue}16></canvas>17
18<script>19 import {20 Chart,21 RadarController,22 RadialLinearScale,23 PointElement,24 LineElement,25 Filler,26 Tooltip,27 } from "chart.js";28
29 // 必要なコンポーネントのみ登録(Tree Shaking)30 Chart.register(31 RadarController,32 RadialLinearScale,33 PointElement,34 LineElement,35 Filler,36 Tooltip,37 );38
39 const canvas = document.getElementById("radar-chart") as HTMLCanvasElement;40 const labels = JSON.parse(canvas.dataset.labels ?? "[]");41 const values = JSON.parse(canvas.dataset.values ?? "[]");42 const maxValue = Number(canvas.dataset.max ?? "5");43
44 new Chart(canvas, {45 type: "radar",46 data: {47 labels,48 datasets: [49 {50 data: values,51 backgroundColor: "rgba(139, 90, 43, 0.2)",52 borderColor: "rgba(139, 90, 43, 1)",53 pointBackgroundColor: "rgba(139, 90, 43, 1)",54 },55 ],56 },57 options: {58 scales: {59 r: {60 min: 0,61 max: maxValue,62 ticks: {63 stepSize: 1,64 },65 },66 },67 },68 });69</script>ポイント:
- Astroコンポーネントのfrontmatter部分(
---内)ではChart.jsをインポートしない - データは
data-*属性でHTMLに埋め込み、クライアントサイドスクリプトで読み取る - Chart.jsは
<script>タグ内でインポートすることで、ビルド時にはバンドルされるがサーバーサイドでは実行されない
問題2: レーダーチャートの0始まり問題
試行錯誤の過程
目盛りの設定で何度も調整が必要でした。
1試行1: 0〜6の6段階 → 6段階は多すぎて見づらい2試行2: -1から8段階 → マイナス値は意味がない3試行3: 0から7段階 → 最大値が大きすぎる4試行4: 最大値5に設定 → 採用「1が0に見える」問題
最も悩んだのは、**スコア1のときにレーダーチャート上で「値がないように見える」**問題でした。
Chart.jsのレーダーチャートはデフォルトで min: 0 の場合、中心が0になります。しかし、スコア1のポイントが中心のすぐ近くにプロットされるため、目視で「0(値なし)」と区別しづらいのです。
1「1が0の位置にみえるので常に0を中心に始まるようにしてください」2→ min: 0 を明示的に設定3
4「チャートの中心を0からはじめることはできませんか?5 1の値のときに値がないように見えます」6→ 最大値を5に抑えることで、1と0の視覚的な差を大きくした最終的な解決策
1options: {2 scales: {3 r: {4 min: 0, // 中心を0に固定5 max: 5, // 最大値を5に設定(6段階: 0,1,2,3,4,5)6 ticks: {7 stepSize: 1,8 display: true,9 },10 pointLabels: {11 font: {12 size: 14,13 },14 },15 },2 collapsed lines
16 },17},max: 5 にすることで、チャートの面積に対するスコア1の占有率が 1/5 = 20% となり、0との違いが視覚的に判別できるようになりました。
問題3: 項目の並び順がおかしくなる
発生した問題
「品質・耐久性・デザイン・コスパ・機能性・サポート」の順で上から右回りに表示したいのに、表示順がずれる問題が発生しました。
原因と解決策
Chart.jsのレーダーチャートは、labels 配列の順番通りに上から時計回りにプロットします。データの並び順が意図と一致しているかを確認する必要があります。
1// 上から右回り: 品質 → 耐久性 → デザイン → コスパ → 機能性 → サポート2const labels = ["品質", "耐久性", "デザイン", "コスパ", "機能性", "サポート"];3const data = [4, 3, 2, 5, 3, 2]; // 各項目に対応するスコア配列の順番を正しく設定するだけで解決しましたが、データソース側の順番が異なる場合はマッピングが必要です。
Astroでの呼び出し
1---2// 商品詳細ページ3import RadarChart from "../components/RadarChart.astro";4
5const flavorProfile = {6 labels: ["品質", "耐久性", "デザイン", "コスパ", "機能性", "サポート"],7 data: [item.quality, item.durability, item.design, item.costPerformance, item.functionality, item.support],8};9---10
11<section>12 <h2>評価チャート</h2>13 <RadarChart14 labels={flavorProfile.labels}15 data={flavorProfile.data}3 collapsed lines
16 maxValue={5}17 />18</section>まとめ
AstroのSSG環境でChart.jsのレーダーチャートを実装する際のポイントをまとめます。
- インポートエラー — Chart.jsはクライアントサイドの
<script>タグ内でインポートする。frontmatter部分では読み込まない - 0始まり問題 —
min: 0を明示的に設定し、maxを適切に抑えることでスコア1と0の視覚的な差を確保する - 並び順 —
labels配列の順番がそのまま上から時計回りの表示順になる - データの受け渡し — Astro propsからHTML
data-*属性を経由してクライアントスクリプトに渡すパターンが安定する
SSGとクライアントサイドライブラリの組み合わせは、ビルド時と実行時の環境の違いを意識する必要がありますが、パターンさえ掴めば安定して使えます。