title: Leaflet with next.js?
body: <p>I am getting a ReferenceError:</p>
  <blockquote>
  <p>window is not defined when using next.js with leaflet.js .</p>
  </blockquote>
  <p>Wondering if there's a simple solution to this problem - is using next.js overcomplicating my workflow?</p>
  <p>for those curious with the exact code,</p>
  <pre><code>import React, { createRef, Component } from &quot;react&quot;;
  import L from &quot;leaflet&quot;;
  import { Map, TileLayer, Marker, Popup, DivOverlay } from &quot;react-leaflet&quot;;
  import axios from &quot;axios&quot;;
  import Header from &quot;./Header&quot;;
  
  
  export default class PDXMap extends Component {
    state = {
      hasLocation: false,
      latlng: {
        lat: 45.5127,
        lng: -122.679565
      },
      geoJSON: null
    };
  
    mapRef = createRef();
  
    componentDidMount() {
      this.addLegend();
      if (!this.state.hasLocation) {
        this.mapRef.current.leafletElement.locate({
          setView: true
        });
      }
      axios
        .get(
          &quot;https://opendata.arcgis.com/datasets/40151125cedd49f09d211b48bb33f081_183.geojson&quot;
        )
        .then(data =&gt; {
          const geoJSONData = data.data;
          this.setState({ geoJSON: geoJSONData });
          return L.geoJSON(this.state.geoJSON).addTo(
            this.mapRef.current.leafletElement
          );
        });
    }
  
    handleClick = () =&gt; {
      this.mapRef.current.leafletElement.locate();
    };
  
    handleLocationFound = e =&gt; {
      console.log(e);
      this.setState({
        hasLocation: true,
        latlng: e.latlng
      });
    };
  
    getGeoJsonStyle = (feature, layer) =&gt; {
      return {
        color: &quot;#006400&quot;,
        weight: 10,
        opacity: 0.5
      };
    };
  
    addLegend = () =&gt; {
      const map = this.mapRef.current.leafletElement;
      L.Control.Watermark = L.Control.extend({
        onAdd: function(map) {
          var img = L.DomUtil.create(&quot;img&quot;);
  
          img.src = &quot;https://leafletjs.com/docs/images/logo.png&quot;;
          img.style.width = &quot;200px&quot;;
  
          return img;
        }
      });
  
      L.control.watermark = function(opts) {
        return new L.Control.Watermark(opts);
      };
  
      L.control.watermark({ position: &quot;bottomleft&quot; }).addTo(map);
    };
  
    render() {
      const marker = this.state.hasLocation ? (
        &lt;Marker position={this.state.latlng}&gt;
          &lt;Popup&gt;
            &lt;span&gt;You are here&lt;/span&gt;
          &lt;/Popup&gt;
        &lt;/Marker&gt;
      ) : null;
  
      return (
        &lt;Map
          className=&quot;map-element&quot;
          center={this.state.latlng}
          length={4}
          onClick={this.handleClick}
          setView={true}
          onLocationfound={this.handleLocationFound}
          ref={this.mapRef}
          zoom={14}
        &gt;
          &lt;TileLayer
            attribution='&amp;amp;copy &lt;a href=&quot;http://osm.org/copyright&quot;&gt;OpenStreetMap&lt;/a&gt; contributors'
            url=&quot;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;
          /&gt;
          {marker}
        &lt;/Map&gt;
      );
    }
  }
  
  /**
   * TODO:  Add Header + Legend to map
   *        - Header to be styled
   *        - Legend to be present in header
   *
   */
  
  
  import React from 'react';
  import PDXMap from &quot;../components/map&quot;;
  
  
  export default function SignIn() {
    const classes = useStyles();
  
    return (
        &lt;PDXMap/&gt;
  );
  }
  </code></pre>
  <p>I'm happy to use any way forward - just interested in getting a functional product.</p>
  <p>Cheers!</p>
  <h3>Update</h3>
  <p>Hey everyone,</p>
  <p>I am still getting this error (came back to this a bit later than I had planned haha).</p>
  <p>I am currently using this approach with useEffects,</p>
  <pre><code>import React, {useEffect, useState} from 'react';
  
  function RenderCompleted() {
  
      const [mounted, setMounted] = useState(false);
  
      useEffect(() =&gt; {
          setMounted(true)
  
          return () =&gt; {
              setMounted(false)
          }
      });
  
      return mounted;
  }
  
  export default RenderCompleted;
  </code></pre>
  <p>and this is the page it is showing on</p>
  <pre><code>import React, { useEffect } from &quot;react&quot;;
  import Router, { useRouter } from &quot;next/router&quot;;
  import { useRef, useState } from &quot;react&quot;;
  
  
  //viz
  import PDXMap from &quot;../../components/Visualization/GIS/map&quot;;
  
  import RenderCompleted from &quot;../../components/utils/utils&quot;;
  
  // import fetch from 'isomorphic-unfetch';
  import { Cookies, CookiesProvider } from &quot;react-cookie&quot;;
  const cookies = new Cookies();
  //containers
  
  // Layouts
  import Layout from &quot;../../components/Layout/Layout_example&quot;;
  import Chart from &quot;../../components/Visualization/Graphs/Chart&quot;;
  import Table from &quot;../../components/Visualization/Tables/Table&quot;;
  import Sidebar from &quot;../../components/Layout/Sidebar/SidebarProperty&quot;;
  
  
  
  export default function Bargains() {
  
    // const [inbrowser, setBrowser] = useState(false);
  
    const choiceRef = useRef&lt;any&gt;();
    const [message, setMessage] = useState&lt;any&gt;(null);
  
    const [productList, setProductList] = useState&lt;any&gt;([]);
    const [searched, setSearched] = useState(false);
  
    const router = useRouter();
  
    let token = cookies.get(&quot;token&quot;);
  
    // useEffect(() =&gt; {
    //   setBrowser(true);
    // });
    const isMounted = RenderCompleted();
  
  
    const columns = React.useMemo(
      () =&gt; [
      ....
      ],
  
      []
    )
  
  
  
    async function handleChoice() {
  
      console.log(&quot;searching...&quot;, choiceRef.current?.value);
      setMessage(&quot;Searching...&quot;);
      var headers = {
        &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,
        &quot;auth-token&quot;: token,
      };
  
      fetch(
      ....
    }
  
  
              &lt;div className=&quot;flex flex-wrap &quot;&gt;
              {isMounted &amp;&amp; &lt;PDXMap/&gt;}
  
  
                
                &lt;Table columns={columns as any} data={productList as any} /&gt;
  
  
              &lt;/div&gt;
            &lt;/div&gt;
  
  
  
          &lt;/div&gt;
        &lt;/div&gt;
  
  
      &lt;/Layout&gt;
  
  
  
    )
  }
  </code></pre>
  <p>With the same error message of</p>
  <pre><code>ReferenceError: window is not defined
  </code></pre>
  <p>##update two</p>
  <p>Okay, so oddly, it does work when I browse into the site from another page, but not when i load the page itself.</p>
  <p>Will have a think on this, but perhaps it is because the map is loading data with componentDidMount() and that is interacting weirdly?</p>
  <h2>Update</h2>
  <p>Okay I've created a more simple example based on <a href="https://github.com/rajeshdh/react-leaflet-with-nextjs" rel="noreferrer">https://github.com/rajeshdh/react-leaflet-with-nextjs</a></p>
  <p>Now it is loading, but the tiles are showing incorrectly, with some tiles not loading.</p>
  <p>This is the map component I am using to be simple,</p>
  <pre><code>import React, { Component, createRef } from 'react';
  import { Map, TileLayer, Marker, Popup, MapControl, withLeaflet } from 'react-leaflet';
  import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch';
  
  
  class SearchBox extends MapControl {
    constructor(props) {
      super(props);
      props.leaflet.map.on('geosearch/showlocation', (e) =&gt; props.updateMarker(e));
    }
  
    createLeafletElement() {
      const searchEl = GeoSearchControl({
        provider: new OpenStreetMapProvider(),
        style: 'bar',
        showMarker: true,
        showPopup: false,
        autoClose: true,
        retainZoomLevel: false,
        animateZoom: true,
        keepResult: false,
        searchLabel: 'search'
      });
      return searchEl;
    }
  }
  
  
  export default class MyMap extends Component {
    state = {
      center: {
        lat: 31.698956,
        lng: 76.732407,
      },
      marker: {
        lat: 31.698956,
        lng: 76.732407,
      },
      zoom: 13,
      draggable: true,
    }
  
    refmarker = createRef(this.state.marker)
  
    toggleDraggable = () =&gt; {
      this.setState({ draggable: !this.state.draggable });
    }
  
    updateMarker = (e) =&gt; {
      // const marker = e.marker;
      this.setState({
        marker: e.marker.getLatLng(),
      });
      console.log(e.marker.getLatLng());
    }
  
    updatePosition = () =&gt; {
      const marker = this.refmarker.current;
      if (marker != null) {
        this.setState({
          marker: marker.leafletElement.getLatLng(),
        });
      }
      console.log(marker.leafletElement.getLatLng());
    }
  
    render() {
      const position = [this.state.center.lat, this.state.center.lng];
      const markerPosition = [this.state.marker.lat, this.state.marker.lng];
      const SearchBar = withLeaflet(SearchBox);
  
      return (
        &lt;div className=&quot;map-root&quot;&gt;
          &lt;Map center={position} zoom={this.state.zoom} style={{
                          height:&quot;700px&quot;
                      }}&gt;
            &lt;TileLayer
              attribution='&amp;amp;copy &lt;a href=&quot;http://osm.org/copyright&quot;&gt;OpenStreetMap&lt;/a&gt; contributors'
              url=&quot;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;
            /&gt;
            &lt;Marker
              draggable={true}
              onDragend={this.updatePosition}
              position={markerPosition}
              animate={true}
              ref={this.refmarker}&gt;
              &lt;Popup minWidth={90}&gt;
                &lt;span onClick={this.toggleDraggable}&gt;
                  {this.state.draggable ? 'DRAG MARKER' : 'MARKER FIXED'}
                &lt;/span&gt;
              &lt;/Popup&gt;
            &lt;/Marker&gt;
            &lt;SearchBar updateMarker={this.updateMarker} /&gt;
          &lt;/Map&gt;
          &lt;style jsx&gt;{`
                  .map-root {
                    height: 100%;
                  }
                  .leaflet-container {
                   height: 400px !important;
                   width: 80%;
                   margin: 0 auto;
                 }
             `}
          &lt;/style&gt;
        &lt;/div&gt;
      );
    }
  }
  </code></pre>
  <p>And to call it I am using this,</p>
  <pre><code>const SimpleExample = dynamic(() =&gt; import(&quot;../../components/Visualization/GIS/map&quot;), {
    ssr: false
  }); 
  </code></pre>
  <p>And have tried this
  {isMounted &amp;&amp; }</p>