Next.jsでMapboxを使って地図を表示する

Next.jsで、Mapboxによる地図を表示するにはどうすればよいか、試行錯誤していましたが、 やっと地図が表示できたので、やり方を纏めておきます。

Mapboxとは

Mapboxは、Web、モバイル、ARなどさまざまなプラットフォームで使える地図サービスです。

公式サイト では、

Mapbox is the location data platform for mobile and web applications. We provide building blocks to add location features like maps, search, and navigation into any experience you create.

と説明されています。

Next.jsで地図を表示する

ここでは、Next.jsのアプリケーションに、Mapboxを使って地図を表示する方法を書いてみます。

(TypeScriptは使っていません(^^; )

Mapboxのアカウントを作成する

Mapboxを使うには、アカウントを作成し、アクセストークンを取得する必要があります。 詳細は割愛します。

react-map-glは使わない

MapboxをReactで使うためのらいぶらりとして、react-map-glというのがありました。これを試してみたところ、Mapboxのオプションで trackResize というオプションがあるのですが、これがfalseに固定されていることが不満で使うのをやめました。

trackResizeオプションは、MapboxのAPI Referrenceで、

If true , the map will automatically resize when the browser window resizes.

と説明されています。つまり、react-map-glを使った場合、Windowのリサイズに地図が自動で追従してリサイズされません。(どうにかすればリサイズできるのでしょうけど... 今の僕にはそんなスキルはありませんでした...)

mapxbox-glをインストールする

Mapboxが提供するnpmパッケージを使用します。

npm install --save mapbox-gl

でインストールします。

地図用コンポーネントを作成する

Mapboxによる地図を表示するだけの単純なコンポーネントを作ります。

components/map.js としました。

結論を言うと、↓のようなコードになりました。

import React from 'react';
import mapboxgl from 'mapbox-gl';

mapboxgl.accessToken = process.env.MAPBOX_TOKEN;

class Map extends React.Component {
  options = {
    container: 'map',
    style: 'mapbox://styles/mapbox/dark-v10',
    center: [137.6850225, 38.258595],
    zoom: 5
  };

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.map = new mapboxgl.Map(this.options);
  }

  render() {
    return(
      <div id="map">
        <div ref={el => this.mapContainer = el} />
      </div>
    )
  }
}

export default Map;

日本のおおよその中心を地図の中心として、表示するようにしています。 Mapboxのアクセストークンは、環境変数として定義しています。 ハマった点は、options.containerに何を指定すればよいか全くわからず、長いこと悩みましたが、 結局地図を埋める要素の親の要素のidを指定することで解決しました。(上の例では、mapを指定しました。)

ページに地図を表示する

地図用コンポーネントができたら、早速ページにコンポーネントを適用してみます。 ここの実装については、

Making Next.js and Mapbox GL JS get along

に助けられました。

この記事によると、

Without diving into great detail, Mapbox GL JS uses the global window object under the hood. When rendering components server-side as we want to do using next.js, this object does not exist.

とあり、つまり、MapboxのJSが必要とするグローバルなwindowオブジェクトが、Next.jsによるSSR時は存在しない(ので、そのままでは使えない)ということのようです。これを回避するために、Next.jsが提供するnext/dynamicというパッケージを使い、地図を表示します。

結果、ページ(pages/index.js)は↓のような実装となりました。

import dynamic from 'next/dynamic';
import '../styles/styles.scss';

const Map = dynamic(() => import('../components/map'), {
  loading: () => <p>Loading...</p>,
  ssr: false
});

export default function Index() {
  return (
    <div id="app">
      <Map />
    </div>
  );
}

これで、無事に地図が表示できるようになりました。


もともと、Next.js + TypeScript + Mapboxで何か作ろうと思っていたのですが、なかなかうまく行かず、右往左往しまいして、一旦TypeScriptを諦めて、なんとかここまで来ました。 おそらく、TypeScript環境下でも地図は表示できるんじゃないかと思えてきました。 もし、内容におかしな点があれば、Twitterなどで指摘頂けるとありがたいです!