react-leaflet
react-leaflet copied to clipboard
Nextjs 15.1.6 Bug : ReferenceError: window is not defined
Bug report in v5
Before opening an issue, make sure to read the contributing guide and understand this is a bug tracker, not a support platform.
Please make sure to check the following boxes before submitting an issue.
Issues opened without using this template will be closed unless they have a good reason not to follow this template.
- [x] All peer dependencies are installed: React, ReactDOM and Leaflet.
- [x] Using the latest stable version of React and ReactDOM v19.
- [x] Using the supported version of Leaflet (v1.9.0 minimum) and its corresponding CSS file is loaded.
- [x] Using the latest v5 version of React-Leaflet.
- [x] The issue has not already been reported.
- [x] Make sure you have followed the quick start guide for Leaflet.
- [x] Make sure you have fully read the documentation and that you understand the limitations.
Expected behavior
When we run the project using npm run dev in Nextjs 15.1.6, there should be no errors on the terminal. When we publish on Vercel it should publish immediately.
Actual behavior
We get the following error when we do npn run dev in Nexjts 15.16
ReferenceError: window is not defined
at (ssr)/./node_modules/leaflet/dist/leaflet-src.js (C:\GithubRepos\flight-tracking.next\server\vendor-chunks\leaflet.js:19:1)
at webpack_require (C:\GithubRepos\flight-tracking.next\server\webpack-runtime.js:33:43)
at (ssr)/./node_modules/react-leaflet/lib/MapContainer.js (C:\GithubRepos\flight-tracking.next\server\vendor-chunks\react-leaflet.js:20:1)
at webpack_require (C:\GithubRepos\flight-tracking.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(ssr)/./app/flight-tracking/page.js:9:71)
at (ssr)/./app/flight-tracking/page.js (C:\GithubRepos\flight-tracking.next\server\app\flight-tracking\page.js:170:1)
at Object.webpack_require [as require] (C:\GithubRepos\flight-tracking.next\server\webpack-runtime.js:33:43)
at JSON.parse (
We get the following error when we push the build to Vercel and build fails:
ReferenceError: window is not defined Error: Command "npm run build" exited with 1
Steps to reproduce
Please provide the simplest example possible to reproduce the issue, based on this StackBlitz.
Could you write a better explicative title of the issue? In order to allow a quick understanding of what's the issue is about, without entering and reading it fully.
Thanks
Could you write a better explicative title of the issue? In order to allow a quick understanding of what's the issue is about, without entering and reading it fully.
Thanks
Done. Somehow that was missed when I reported while saving.
+1. Have the same issue with NextJS 15.1.6 and react 19.0.0.
@silversonicaxel Any update on this please ? It is not allowing to build the application and therefore the severity is critical
After checking the library a little more, it is said here that they don't support SSR... I guess we won't see a check for
typeof window == 'undefined'
anytime soon.
I forked the project but cannot get it working due to a weird tsconfig, I will keep you updated if I manage to make it work
We are using on client side inside useEffect. We are not using this on server side. Hope this information helps
@prashantchothani I am not in reactleaflet team :)
+1 Having the same issue for me in Next.js 15.1.6
It happens to me as well.
I have this message:
⨯ ReferenceError: window is not defined
at (ssr)/./node_modules/leaflet/dist/leaflet-src.js (.next/server/vendor-chunks/leaflet.js:19:1)
at __webpack_require__ (.next/server/webpack-runtime.js:33:43)
at (ssr)/./node_modules/react-leaflet/lib/MapContainer.js (.next/server/vendor-chunks/react-leaflet.js:20:1)
at __webpack_require__ (.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(ssr)/./src/components/map.tsx:9:71)
at (ssr)/./src/components/map.tsx (.next/server/app/page.js:170:1)
at Object.__webpack_require__ [as require] (.next/server/webpack-runtime.js:33:43) {
digest: '2253214984'
For NextJS users, dynamically export your map
index.ts
'use client'
import dynamic from 'next/dynamic'
const MyMap = dynamic(() => import('./MyMap'), {
ssr: false,
}).
export { MyMap }
MyMap.tsx
'use client';
import { MapContainer } from 'react-leaflet'
const MyMap = () => <MapContainer>...</MapContainer>
FYI:
The dynamic import with ssr: false disables server-side rendering for the <App /> component, making it truly client-only (SPA). https://nextjs.org/docs/app/guides/migrating/from-create-react-app#step-7-add-a-client-only-entrypoint
Edit (May 30): Add FYI and add MyMap.tsx to make the comment less ambiguous. nextjs -> NextJS
@nwatab It seems that directly importing in a server component isn’t working. As a result, the component needs to be wrapped by another component. Is there a way to export a component without needing to wrap it?
As far as I know, re-exporting is the only way. I also tried React.lazy and Suspense, which didn't work in my NextJS15 environment.
For nextjs users, dynamically export your map
'use client' import dynamic from 'next/dynamic' const MyMap = dynamic(() => import('./MyMap'), { ssr: false, }). // const MyMap = () => <MapContainer>...</MapContainer> export { MyMap }
I'm having trouble getting this to work. Did this work for most people? I'm having this same error.
It is working on my end with the following wrapper:
// /components/map/Map.tsx
'use client';
import { MapContainer } from 'react-leaflet'
import { TileLayer } from 'react-leaflet'
import "../../../../node_modules/leaflet/dist/leaflet.css"
const Map = () => {
if(typeof window === 'undefined'){
return <></>
}
return (
<div>
<MapContainer center={} zoom={9} scrollWheelZoom={scroll} className="h-[60vh] w-[90vw] justify-self-center px-10 m-0 z-0">
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</MapContainer>
</div>
)
}
export default Map;
// /components/map/NextjsMap.tsx
'use client'
import dynamic from 'next/dynamic'
const DynamicMap = dynamic(() => import('./Map.tsx'), {
loading: () => <p>Loading...</p>,
ssr: false,
})
export default function NextjsMap() {
return <DynamicMap />
}
Please note that this code can probably be optimised, and that there might not even be a need for a wrapper like this, or that everything could be in the same file, but at least it's working. Also, I am using TailwindCSS so please change the h-[60vh] etc... by whatever you use to set a height, or setup a custom CSS class
any news on this ?
@PaulLeCam would you mind if I try to address it?
@dolgachio If you figure out an easy fix, let me know! We would appreciate it greatly as we still haven't been able to solve it in our project.
@DylanDevelops I am not sure this one will be an easy fix, but I can investigate. But it would be nice to know that nobody else is working on it right now.
I was running into this issue, and came up with a fix in the meantime. I basically checked if window was defined inside useEffect and only rendered the map at that time (stored in a state). Being a client component, this was sufficient for me and allowed the page containing the map to build and render on production. I hope this helped, I'm sure there are better ways to do this... 😅
Caller Component (outer):
'use client'
import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
const Map = dynamic(() => import('./MapComponent'), {
ssr: false,
})
export default function AboutMap({ lat, lon }) {
const [mapVar, setMapVar] = useState(null);
useEffect(() => {
if (typeof window !== 'undefined') {
setMapVar(
<div className='map-container md:row-span-2 max-sm:row-span md:h-96 min-h-96 bento observe-scroll z-0'>
<Map lat={lat} lon={lon} />
</div>
);
}
}, [lat, lon]);
return <>{mapVar}</>;
}
Map component:
'use client'
import 'leaflet/dist/leaflet.css'
import { Icon } from 'leaflet'
import React from 'react'
import { useEffect, useRef } from 'react';
import { MapContainer, Marker, TileLayer } from 'react-leaflet'
const Map = (props) => {
let mapRef = useRef()
useEffect(() => {
if (mapRef.current) {
mapRef.current.setView([props.lat, props.lon]);
}
}, [props.lat, props.lon]);
let mapUrl = "your map url"
return (
<MapContainer preferCanvas={true} center={[props.lat, props.lon]} zoom={6} scrollWheelZoom={false} ref={mapRef} tapTolerance={100}>
<TileLayer
url={mapUrl}
/>
<Marker position={[props.lat, props.lon]} icon={new Icon({ iconUrl: "/marker-icon-2x.png", iconSize: [25, 41], iconAnchor: [12, 41] })} />
</MapContainer>
);
}
export default Map
@caerroff, @alextenczar and @nwatab for the win!
It turned out that the solution by @alextenczar was almost identical to what I had setup in my project. So, I went with that and bingo...problem solved. It seems so crazy simple now that I understand the problem. At least I learned abit more about NextJS at the expense of a couple of hair follicles. ;-)
Here is my take on the solution and making it very basic and simple.
After getting your map component all spiffy and sparkly <MySpiffyMapComponent/>, you need to import it dynamically and use it in a SECOND component to display to the world. <HelloWorldComponent/>
export default function MySpiffyMapComponent() {
return (
<MapContainer>
<TileLayer url='https://somemap.com/.png' />
<Marker icon={spiffyImg} position={someGeoCoordinates}>
<Tooltip>
<div>Some nifty tooltip stuff</div>
</Tooltip>
<Popup>
<div>A really spiffy popup</div>
</Popup>
</Marker>
</MapContainer>
);
}
And where the magic happens:
import dynamic from 'next/dynamic';
//the constant name can be anything you want
const MySpiffyMapComponent = dynamic(() => import('@/app/components/MySpiffyMapComponent'), {
ssr: false,
})
export default function HelloWorldComponent() {
return (
<div>
<MySpiffyMapComponent/>
</div>
)
}
I hope this can help somebody out there.
Cheers!
Hi all, I investigated a little bit, and it seams like it is not that ease to address the cause.
In the Leaflet library itself there is a usage of window object in the script root without any wrapper.
So, my assumtion is that if you just import leaflet, this simple import breaks SSR.
However, Leaflet library will release V2 version which modernizes all the approaches used inside. I checked the source code and this issue could be resolved by using Leaflet V2.
But it will take time, cause they have just recently released and alpha version https://github.com/Leaflet/Leaflet/releases/tag/v2.0.0-alpha
After they release a stable version, I volunteer to update it here :)
Thanks to @steve-miller-javajockey and @alextenczar for their solutions. They fixed it for me