You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS13集成react-leaflet报Window is not defined,use client无效

在Next.js 13中解决react-leaflet的"window is not defined"错误

问题场景

在Next.js 13中使用react-leaflet组件时,即使在文件顶部添加了'use client'指令,也通过typeof window !== 'undefined'做了环境判断,依然出现"window is not defined"错误。

原页面代码(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'

解决方法

问题根源在于leaflet库在模块导入阶段就直接访问了window对象,而Next.js 13中即使页面加了'use client',模块导入过程仍会在服务端执行,导致报错。需要通过动态导入并禁用SSR来解决:

步骤1:将地图逻辑拆分到单独的客户端组件

创建components/CensusMapClient.tsx,把所有和react-leaflet相关的代码移到这里,确保顶部有'use client':

'use client';

import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet';
import CensusTractData from "../pages/data/census-tracts_min.json";
import 'leaflet/dist/leaflet.css';
import type {GeoJsonObject, Feature, Geometry} from "geojson";

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,
  };
};

export default function CensusMapClient() {
  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={censusTractStyle} />
      </MapContainer>
    </div>
  );
}

步骤2:在页面中动态导入该客户端组件

修改pages/censusmap.tsx,使用Next.js的dynamic函数导入地图组件,并设置ssr: false,确保组件只在客户端渲染:

import { type NextPage } from "next";
import dynamic from 'next/dynamic';

// 动态导入地图组件,禁用服务端渲染
const CensusMapClient = dynamic(() => import('../components/CensusMapClient'), {
  ssr: false,
  loading: () => <div>加载地图中...</div> // 可选:添加加载状态提示
});

const CensusTractMap: NextPage = () => {
  return (
    <>
      <CensusMapClient />
    </>
  );
};

export default CensusTractMap;

原理说明

通过dynamic导入并设置ssr: false,Next.js会跳过该组件的服务端渲染过程,仅在浏览器环境中加载和执行组件代码,从根源避免了服务端访问window对象的问题。

内容的提问来源于stack exchange,提问作者avenmia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:50:23