mapbox-web-integration-patterns について
mapbox-web-integration-patternsは、ウェブアプリへのMapbox GL JSとMapbox Search JSの組み込み方を示すエージェントスキルです。複数のフロントエンドフレームワークを対象に、ライフサイクル管理、トークン処理、クリーンアップ、よくある統合ミスを扱います。
主な機能
- 01React、Vue、Svelte、Angularの統合パターン
- 02Vanilla JavaScriptとNext.jsのガイド
- 03Mapbox Search JSの統合
- 04マップのライフサイクルとクリーンアップ
- 05トークン管理とよくあるエラーの確認
活用シーン
- 01ウェブアプリへのMapbox GL JS導入
- 02フロントエンド向けマップコンポーネントの作成
- 03Mapbox Search JSの統合
- 04マップのライフサイクルやスタイル切替の問題調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mapbox/ mapbox-agent-skills --skill mapbox-web-integration-patternsインストール後、Claude Code で/mapbox-web-integration-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdevals/evals.jsonreferences/angular.mdcommon-mistakes.mdnextjs.mdsvelte.mdtoken-management.mdvanilla.mdvue.mdweb-components.md詳細
Mapbox 統合パターンのスキル
このスキルでは、React、Vue、Svelte、Angular、バニラ JavaScript を使って Web アプリケーションに Mapbox GL JS を統合するための公式パターンを紹介します。これらのパターンは、Mapbox の create-web-app スキャフォールディングツールに基づいており、本番環境ですぐに使えるベストプラクティスを示しています。
バージョン要件
Mapbox GL JS
推奨: v3.x(最新)
- 最小バージョン: v3.0.0
- v3.x の理由: モダンな API、パフォーマンスの向上、活発な開発
- v2.x: レガシー版。現在は積極的に開発されていません(下記の移行に関する注意事項を参照)
npm でのインストール(本番環境に推奨):
npm install mapbox-gl@^3.0.0 # Installs latest v3.x
CDN(プロトタイピング専用):
<script src="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.css" rel="stylesheet" />
フレームワーク要件
React: GL JS は React 16.8 以降で動作します(フックが必要です)。create-web-app は React 19.x を使ってスキャフォールディングします。
Vue: GL JS は Vue 2.x 以降で動作します(Vue 3 Composition API を推奨)。
Svelte: GL JS はすべての Svelte バージョンで動作します。create-web-app は Svelte 5.x を使ってスキャフォールディングします。
Angular: GL JS は Angular 2 以降で動作します。create-web-app は Angular 19.x を使ってスキャフォールディングします。
Next.js: 最小バージョンは 13.x(App Router)、Pages Router の場合は 12.x 以降です。
Mapbox Search JS
npm install @mapbox/search-js-react@^1.0.0 # React
npm install @mapbox/search-js-web@^1.0.0 # Other frameworks
バージョン移行に関する注意事項(v2.x から v3.x)
- WebGL 2 が必須になりました
optimizeForTerrainオプションが削除されました- TypeScript の型が改善され、ツリーシェイキングのサポートが向上しました
- コアの初期化パターンに破壊的変更はありません
トークンのパターン(v2.x と v3.x の両方で動作):
const token = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN; // Use env vars in production
// Global token (works since v1.x)
mapboxgl.accessToken = token;
const map = new mapboxgl.Map({ container: '...' });
// Per-map token (preferred for multi-map setups)
const map = new mapboxgl.Map({
accessToken: token,
container: '...'
});
基本原則
Mapbox GL JS の統合では、必ず以下を行ってください。
- 適切なライフサイクルフックでマップを初期化する
- マップインスタンスをコンポーネントの状態に保存する(レンダーごとに再作成しない)
- メモリリークを防ぐため、クリーンアップ時に必ず
map.remove()を呼び出す - トークンを安全に管理する(環境変数を使用)
- CSS をインポートする:
import 'mapbox-gl/dist/mapbox-gl.css'
React 統合(主要パターン)
パターン:useRef + useEffect とクリーンアップ
注: これらの例では、
create-web-appで使用されているバンドラーである Vite を使用しています。Create React App を使用する場合は、import.meta.env.VITE_MAPBOX_ACCESS_TOKENをprocess.env.REACT_APP_MAPBOX_TOKENに置き換えてください。その他のバンドラーについては、トークン管理パターンを参照してください。
import { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
function MapComponent() {
const mapRef = useRef(null); // Store map instance
const mapContainerRef = useRef(null); // Store DOM reference
useEffect(() => {
mapboxgl.accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
center: [-71.05953, 42.3629],
zoom: 13
});
// CRITICAL: Cleanup to prevent memory leaks
return () => {
mapRef.current.remove();
};
}, []); // Empty dependency array = run once on mount
return <div ref={mapContainerRef} style={{ height: '100vh' }} />;
}
要点:
- マップインスタンスとコンテナーの両方に
useRefを使用する - 空の依存配列
[]を指定してuseEffect内で初期化する - 必ず
map.remove()を呼び出すクリーンアップ関数を返す - レンダー内でマップを初期化しない(無限ループの原因になります)
React + Search JS
import { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import { SearchBox } from '@mapbox/search-js-react';
import 'mapbox-gl/dist/mapbox-gl.css';
const accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
const center = [-71.05953, 42.3629];
function MapWithSearch() {
const mapRef = useRef(null);
const mapContainerRef = useRef(null);
const [inputValue, setInputValue] = useState('');
useEffect(() => {
mapboxgl.accessToken = accessToken;
mapRef.current = new mapboxgl.Map({
container: mapContainerRef.current,
center: center,
zoom: 13
});
return () => {
mapRef.current.remove();
};
}, []);
return (
<>
<div
style={{
margin: '10px 10px 0 0',
width: 300,
right: 0,
top: 0,
position: 'absolute',
zIndex: 10
}}
>
<SearchBox
accessToken={accessToken}
map={mapRef.current}
mapboxgl={mapboxgl}
value={inputValue}
proximity={center}
onChange={(d) => setInputValue(d)}
marker
/>
</div>
<div ref={mapContainerRef} style={{ height: '100vh' }} />
</>
);
}
Search JS 統合の概要
インストール:
npm install @mapbox/search-js-react # React
npm install @mapbox/search-js-web # Vanilla/Vue/Svelte
どちらのパッケージにも依存関係として @mapbox/search-js-core が含まれています。カスタム検索 UI を構築する場合に限り、-core を直接インストールしてください。
主な設定オプション:
accessToken:Mapbox の公開トークンmap:マップインスタンス(先に初期化する必要があります)mapboxgl:mapboxgl ライブラリへの参照proximity:結果を地理的に絞り込むための[lng, lat]marker:結果マーカーを表示または非表示にするブール値placeholder:検索ボックスのプレースホルダーテキスト
検索ボックスの配置
絶対位置指定(オーバーレイ):
<div
style={{
position: 'absolute',
top: 10,
right: 10,
zIndex: 10,
width: 300
}}
>
<SearchBox {...props} />
</div>
一般的な配置:
- 右上:
top: 10px, right: 10px - 左上:
top: 10px, left: 10px - 左下:
bottom: 10px, left: 10px
よくある間違い(重要)
間違い 1:map.remove() の呼び出し忘れ
// BAD - Memory leak!
useEffect(() => {
const map = new mapboxgl.Map({ ... })
// No cleanup function
}, [])
// GOOD - Proper cleanup
useEffect(() => {
const map = new mapboxgl.Map({ ... })
return () => map.remove() // Cleanup
}, [])
理由: マップインスタンスを作成するたびに WebGL コンテキスト、イベントリスナー、DOM ノードが作成されます。クリーンアップしないと、これらが蓄積してメモリリークを引き起こします。
間違い 2:レンダー内でマップを初期化する
// BAD - Infinite loop in React!
function MapComponent() {
const map = new mapboxgl.Map({ ... }) // Runs on every render
return <div />
}
// GOOD - Initialize in effect
function MapComponent() {
useEffect(() => {
const map = new mapboxgl.Map({ ... })
}, [])
return <div />
}
理由: React コンポーネントは頻繁に再レンダーされます。レンダーごとに新しいマップを作成すると、無限ループやクラッシュの原因になります。
間違い 3:マップインスタンスを適切に保存しない
// BAD - map variable lost between renders
function MapComponent() {
useEffect(() => {
let map = new mapboxgl.Map({ ... })
// map variable is not accessible later
}, [])
}
// GOOD - Store in useRef
function MapComponent() {
const mapRef = useRef()
useEffect(() => {
mapRef.current = new mapboxgl.Map({ ... })
// mapRef.current accessible throughout component
}, [])
}
理由: レイヤーやマーカーの追加、remove() の呼び出しなどの操作には、マップインスタンスにアクセスする必要があります。
Mistake 4: Vue の data() にマップインスタンスを保存する(Vue 固有)
// BAD - Vue's reactivity wraps data() objects in a Proxy, breaking mapbox-gl internals!
export default {
data() {
return {
map: null // Will be wrapped in a Proxy
}
},
mounted() {
this.map = new mapboxgl.Map({ ... }) // Proxy breaks GL internals
}
}
// GOOD - Assign map as a plain instance property, not in data()
export default {
mounted() {
this.map = new mapboxgl.Map({
container: this.$refs.mapContainer,
center: [-71.05953, 42.3629],
zoom: 13
})
},
unmounted() {
this.map?.remove()
}
}
理由: Vue(特に Vue 3)では、リアクティブにするために data() のプロパティが Proxy でラップされます。Mapbox GL JS は内部でオブジェクトの同一性を確認し、プロキシでラップすると維持されないプロパティを使用します。マップを data() に保存すると、気付きにくくデバッグが困難な不具合につながります。代わりに、mounted() でマップインスタンスを this.map に直接代入してください。data() の外で代入されたプロパティはリアクティブになりません。
Mistake 5: スタイルやタイルのエラーを見逃す(エラーハンドラーがない)
// BAD — blank map when the token/style fails
const map = new mapboxgl.Map({ ... });
// GOOD — surface failures
map.on('error', (e) => {
console.error(e.error || e);
// optionally show an on-page error message
});
Mistake 6: jsDelivr の +esm によって deck.gl の CDN が機能しない
<script type="module">
import { MapboxOverlay } from 'https://cdn.jsdelivr.net/npm/@deck.gl/[email protected]/+esm';
</script>
<script src="https://unpkg.com/[email protected]/dist.min.js"></script>
<script>
const { MapboxOverlay, ScatterplotLayer } = deck;
map.addControl(
new MapboxOverlay({
interleaved: false,
layers: [
/* ... */
]
})
);
</script>
マップコントロールとして、単独の Deck ではなく MapboxOverlay(Mapbox IControl)を使用してください。
Mistake 7: draw.create のない描画ツールバー
mapbox-gl-draw を読み込む場合は、draw.create を監視してください(また、draw.getAll() を使って UI を更新してください)。削除が不完全なハンドラーが末尾に }); を残すと、ページがクラッシュします。
Mistake 8: setStyle 後にレイヤーが失われる(style.load の再バインドがない)
map.setStyle(...) はスタイルツリーを置き換えます。それ以前に追加したカスタムソース、レイヤー、ハンドラーは、再設定しない限り消去されます。
function onStyleReady() {
// re-add sources, layers, and interaction handlers here
}
map.on('style.load', onStyleReady);
document.querySelectorAll('[data-style]').forEach((btn) => {
btn.addEventListener('click', () => {
map.setStyle(btn.dataset.style);
// do NOT only add layers on the first 'load' — wait for style.load after every switch
});
});
エージェントのアンチパターン: setStyle を一度だけ呼び出し、style.load の再バインドを行わないスタイル切り替えボタン。最初のスタイルは機能しますが、その後の切り替えはすべて動作しないように見えます。
リファレンスファイル
フレームワーク固有のパターンや詳細については、次のファイルを読み込んでください。
references/vue.md— Vue との統合(mounted/unmounted のライフサイクル)references/svelte.md— Svelte との統合(onMount/onDestroy)references/angular.md— SSR 処理を含む Angular との統合references/vanilla.md— Vanilla JS(Vite)および Vanilla JS(CDN)references/web-components.md— Web Components(基本、リアクティブ、および React/Vue/Svelte での使用)references/nextjs.md— Next.js App Router および Pages Routerreferences/common-mistakes.md— よくあるミス 4~7 およびテストパターンreferences/token-management.md— バンドラーごとのトークン管理およびスタイル設定
このスキルを使用するタイミング
次の場合にこのスキルを呼び出してください。
- 新しいプロジェクトで Mapbox GL JS をセットアップする
- 特定のフレームワーク(React、Vue、Svelte、Angular、Next.js)に Mapbox を統合する
- フレームワークに依存しない Web Components を構築する
- コンポーネントライブラリ向けに再利用可能なマップコンポーネントを作成する
- マップの初期化に関する問題をデバッグする
- Mapbox Search の機能を追加する
- 適切なクリーンアップとライフサイクル管理を実装する
- フレームワーク間で変換する(例:React から Vue)
- Mapbox 統合のベストプラクティスに沿っているかコードを確認する
関連スキル
- mapbox-cartography:マップデザインの原則とスタイリング
- mapbox-token-security:トークン管理とセキュリティ
- mapbox-style-patterns:一般的なマップスタイルのパターン