Search code examples
reactjstypescriptnext.jsreact-leafletnext.js13

ReferenceError: Window is not defined NextJS13 use Client not working


I'm trying to use NextJS 13 with react-leaflet. When I run my component I get window is not defined. I tried using the "use client" at the top of the file and doing a check to see if "typeof window !== undefined" but still no luck. I'm not using the isBrowser function in the snippet before, but that's what I used originally.

pages/censusmap.tsx

'use client';

import { type NextPage } from "next";
import { MapContainer,  TileLayer, GeoJSON } from 'react-leaflet';
// TODO: Fix this import to be from geojson and change the filename
import CensusTractData from "./data/census-tracts_min.json";
import 'leaflet/dist/leaflet.css';
import type {GeoJsonObject, Feature, Geometry} from "geojson";

const isBrowser = () => typeof window !== 'undefined';

const CensusTractMap: NextPage = () => {
  const getCensusTractFillColor = (population2020: number) => {
    if(population2020 <= 2500)
    {
      return "#bd93f9"
    }
    if(population2020 > 2500 && population2020 < 5000) {
      return "#ffb86c"
    }
    if (population2020 > 5000 && population2020 < 10000)
    {
      return "#8be9fd";
    }
    return undefined;
  }
  const censusTractStyle = (val: Feature<Geometry, {pop20: number}> | undefined) => {
    if(!val)
    {
      return {};
    }
    const pop20 = Number(val.properties.pop20);
    return {
                fillColor: getCensusTractFillColor(pop20),
                color: '#44475a',
                weight: 0.5,
                opacity: 1,
                fillOpacity: 0.4,
            }
  }
  return (
    <>
       <div id="map" className="h-180px">
      <MapContainer center={[20.5, -157.510857]} zoom={7} scrollWheelZoom={true}>
        <TileLayer
          attribution='&copy; "Map data © OpenStreetMap contributors, Esri Community Maps contributors, Map layer by Esri'
          url="https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}"
        />
        <GeoJSON data={CensusTractData as GeoJsonObject} style={(val: Feature<Geometry, {pop20: number}> | undefined) => censusTractStyle(val)}/>
      </MapContainer>
    </div>
    </>
  )
}

export default CensusTractMap
error - ReferenceError: window is not defined
    at ...\node_modules\leaflet\dist\leaflet-src.js:230:19
    at ...\node_modules\leaflet\dist\leaflet-src.js:7:66
    at Object.<anonymous> (...\node_modules\leaflet\dist\leaflet-src.js:10:3)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:170:29)
    at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15) {
  page: '/censusmap'

Solution

  • I think react-leaflet uses window and when you import it ,it will use window (as you know in ssr window is undefiend)

    so add all leaflet related codes to a component and use dynamic import instead

    like this :

    pages/censusmap.tsx

    const CensusmapComponent = dynamic(() => import('../components/CensusmapComponent'), {
      loading: () => 'Loading...',
      ssr: false,
    })
    
    return (<CensusmapComponent/>)
    

    GensusmapComponent:

    import { MapContainer,  TileLayer, GeoJSON } from 'react-leaflet';
    // TODO: Fix this import to be from geojson and change the filename
    import CensusTractData from "./data/census-tracts_min.json";
    import type {GeoJsonObject, Feature, Geometry} from "geojson";
    
    const isBrowser = () => typeof window !== 'undefined';
    
    const CensusTractMapComponent: React.FC = () => {
      const getCensusTractFillColor = (population2020: number) => {
        if(population2020 <= 2500)
        {
          return "#bd93f9"
        }
        if(population2020 > 2500 && population2020 < 5000) {
          return "#ffb86c"
        }
        if (population2020 > 5000 && population2020 < 10000)
        {
          return "#8be9fd";
        }
        return undefined;
      }
      const censusTractStyle = (val: Feature<Geometry, {pop20: number}> | undefined) => {
        if(!val)
        {
          return {};
        }
        const pop20 = Number(val.properties.pop20);
        return {
          fillColor: getCensusTractFillColor(pop20),
          color: '#44475a',
          weight: 0.5,
          opacity: 1,
          fillOpacity: 0.4,
        }
      }
      return (
          <>
            <div id="map" className="h-180px">
              <MapContainer center={[20.5, -157.510857]} zoom={7} scrollWheelZoom={true}>
                <TileLayer
                    attribution='&copy; "Map data © OpenStreetMap contributors, Esri Community Maps contributors, Map layer by Esri'
                    url="https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}"
                />
                <GeoJSON data={CensusTractData as GeoJsonObject} style={(val: Feature<Geometry, {pop20: number}> | undefined) => censusTractStyle(val)}/>
              </MapContainer>
            </div>
          </>
      )
    }
    
    export default CensusTractMapComponent
    

    and CSS files must be added to _app.js (if it's not module)

    in _app.js

     import 'leaflet/dist/leaflet.css';