react-globe.gl
react-globe.gl copied to clipboard
Next.js ES Module Loading Error
Describe the bug
I was trying to use react-globe.gl in a next.js project. I simply created a component, imported import Globe from 'react-globe.gl'
, and instantiated the component with default properties return <Globe>
. However, I get the following compile error:
error - Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: D:\CalebArulandu\Projects\Startups\Glocal\proj\app\node_modules\three\examples\jsm\renderers\CSS2DRenderer.js
require() of ES modules is not supported.
require() of D:\CalebArulandu\Projects\Startups\Glocal\proj\app\node_modules\three\examples\jsm\renderers\CSS2DRenderer.js from D:\CalebArulandu\Projects\Startups\Glocal\proj\app\node_modules\globe.gl\dist\globe.gl.common.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package
scope as ES modules.
Instead rename CSS2DRenderer.js to end in .cjs, change the requiring code
to use import(), or remove "type": "module" from D:\CalebArulandu\Projects\Startups\Glocal\proj\app\node_modules\three\examples\jsm\package.json.
Ironically, if I comment the <Globe> line and then uncomment, hot reloading displays the globe. But, when I manually reload, I get the same error again.
To Reproduce Steps to reproduce the behavior:
- Make a next.js app with typescript template.
- Create a component that returns the
<Globe>
component fromreact-globe.gl
. - Add this component to the
index.tsx
page. - See error
Expected behavior A globe should be displayed during hot reload and after manual reloads.
Desktop (please complete the following information):
- OS: Windows 10
- Browser: Chrome
- Version: 102.0.5005.115 (Official Build) (64-bit)
Additional context Add any other context about the problem here.
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*":["src/*"],
"~/*": ["./*"]
},
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
package.json
{
"name": "app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "12.1.6",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-globe.gl": "^2.22.1"
},
"devDependencies": {
"@types/node": "17.0.42",
"@types/react": "18.0.12",
"@types/react-dom": "18.0.5",
"eslint": "8.17.0",
"eslint-config-next": "12.1.6",
"typescript": "4.7.3"
}
}
@Claeb101 please see: https://github.com/vasturiano/react-globe.gl/issues/1 https://github.com/vasturiano/react-globe.gl/issues/15
Hello @vasturiano . After using the dynamic import method described in the above issues, I was able to get the globe working. However, for some reason, the points layer does not seem to be working. When I serve the following test.html
, the points layer displays, but when I do the same thing in globe.tsx
the points do not render though the textured globe does.
globe.tsx
import dynamic from "next/dynamic";
const Globe = dynamic(() => import('react-globe.gl'), { ssr: false })
const World = () => {
const volcanoes = [
{
"name": "Abu",
"country": "Japan",
"type": "Shield",
"lat": 34.5,
"lon": 131.6,
"elevation": 641
},
{
"name": "Acamarachi",
"country": "Chile",
"type": "Stratovolcano",
"lat": -23.3,
"lon": -67.62,
"elevation": 6046
}]
return (
<Globe
globeImageUrl="//unpkg.com/three-globe/example/img/earth-night.jpg"
backgroundImageUrl="//unpkg.com/three-globe/example/img/night-sky.png"
pointsData={volcanoes}
pointLat="lat"
pointLng="lon"
/>
);
}
export default World
test.html
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/react/umd/react.production.min.js"></script>
<script src="//unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="//unpkg.com/babel-standalone"></script>
<script src="//unpkg.com/react-globe.gl"></script>
<!--<script src="../../dist/react-globe.gl.js"></script>-->
</head>
<body>
<div id="globeViz"></div>
<script type="text/jsx">
const World = () => {
const volcanoes = [
{
"name": "Abu",
"country": "Japan",
"type": "Shield",
"lat": 34.5,
"lon": 131.6,
"elevation": 641
},
{
"name": "Acamarachi",
"country": "Chile",
"type": "Stratovolcano",
"lat": -23.3,
"lon": -67.62,
"elevation": 6046
}]
return <Globe
globeImageUrl="//unpkg.com/three-globe/example/img/earth-night.jpg"
backgroundImageUrl="//unpkg.com/three-globe/example/img/night-sky.png"
pointsData={volcanoes}
pointLat="lat"
pointLng="lon"
/>;
};
ReactDOM.render(
<World />,
document.getElementById('globeViz')
);
</script>
</body>
Do you have any advice on how to troubleshoot this? I can send my zipped project if needed. Thank you!
Weirdly, when I change the dynamic import to the following (as commented in #15), the points layer shows (during hot reload), but on manual reloads I get a hydration error.
let Globe = () => null;
if (typeof window !== "undefined") Globe = require("react-globe.gl").default;
Hello! You can try to render globe with dynamic import like this:
const GlobeComponent = dynamic(() => import("@/components/Globe"), { ssr: false })
and make sure you have "three": "^0.140.0", dependency.
"three": "^0.140.0"
I have set "three": "^0.140.0"
dependency, but am still unable to useRef to change controls.
Example Code
useEffect(() => {
if(globeEl.current){
globeEl.current.controls().enableZoom = false;
globeEl.current.controls().autoRotate = true;
globeEl.current.controls().autoRotateSpeed = 5;
}
}, []);
Am still getting ref undefined error
Unhandled Runtime Error
TypeError: globeEl.current.controls is not a function
Any update on this? Still having the issue.
Any update on this? Still having the issue.
Just use any of lazy loading modes with React or dynamic import with Next.js. The reason you getting undefined error, that 'three.js' lib not rendered 3d object at all and you trying to access them, to prevent this situations put you object into component and lazy load it, so you will be sure that component rendered object and returned it.