1
votes

I wanna use Leaflet with NextJS(typescript).

But Leaflet dose not supported SSR. So, I use react-leaflet-univarsal.

Then, I custom Marker Component of Leaflet. So, I wanna use Leaflet.Icon. I tried 2 things.

  1. if(process.browser){}

This is not found window.

  1. use dynamic import with next/dynamic
let iconPerson: any;
  const DynamicComponent = dynamic(
    () =>
      import('leaflet').then(L => {
        iconPerson = (L as any).Icon.extend({
          options: {
            iconUrl: '/images/icon1.jpg',
            iconRetinaUrl: '/images/icon1.jpg',
            iconSize: new (L as any).Point(60, 75),
            className: 'leaflet-div-icon',
          },
        });
      }) as any,
    { ssr: false },
  );

....

<Marker icon={iconPerson}>

This is printed. > Cannot read property 'createIcon' of undefined

Is the way use L.icon with NextJS?

2

2 Answers

4
votes

I resolved this problem by SSR Map component.

In particular,

import * as React from 'react';
import { Map as M, TileLayer, Marker, Popup } from 'react-leaflet-universal';
import L from 'leaflet';
import { Pois } from '../types/pois';

const iconPerson = new L.Icon({
  iconUrl: '/images/icon1.svg',
  iconRetinaUrl: '/images/icon1.svg',
  iconAnchor: [20, 40],
  popupAnchor: [0, -35],
  iconSize: [40, 40],
});

const MAXIMUM_ZOOM = 18;

type Props = {
  position: { lat: number; lng: number };
  pois: Pois[];
};

const Map: React.FC<Props> = ({ position, pois }) => (
  <M center={position} zoom={MAXIMUM_ZOOM} doubleClickZoom={false}>
    <TileLayer
      attribution='copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <Marker position={position} />
    {pois.map((poi: any) => (
      <Marker position={{ lat: poi.geo.x, lng: poi.geo.y }} icon={iconPerson} key={poi.id}>
        <Popup>{poi.description}</Popup>
      </Marker>
    ))}
  </M>
);

export default Map;

const Map: any = dynamic(() => import('./Map') as any, { ssr: false });

return <Map ... />

Thank you.

1
votes

Dynamically loading is actually just for components, not libraries.

Libraries that require use of client side APIs will break in SSR but there is a way to get around this.

First, import the library like normal and use it in an effect. Effects only fire on the client after the component is mounted.

If that doesn't work, you can import the library in the effect.

Note that componentDidMount() is also viable if you aren't using a functional component.

The equivalent for componentDidMount() in this scenario (see relevant react docs on equivalents) would be to use an empty array:

useEffect(() => {
/* import the library like normal here if necessary */
/* use the library here */
}, []);

For more information you can see FAQ.