ReferenceError: document is not defined (with useLoader)
Closed this issue · 2 comments
When I run npm run build
, I get this error on a nextjs application: "ReferenceError: document is not defined" so the build is cancelled due to this error
My code:
"use client";
import { Canvas, useLoader } from "@react-three/fiber";
import * as THREE from "three";
export default function Page() {
const backgroundImage = useLoader(THREE.TextureLoader, "/bg.png");
return (
<Canvas scene={{ background: backgroundImage }}>
{/* blabla */}
</Canvas>
);
}
When I remove this part the build works correctly
const backgroundImage = useLoader(THREE.TextureLoader, "/bg.png");
My dependencies:
"dependencies": {
"@react-three/drei": "^9.112.0",
"@react-three/fiber": "^8.17.7",
"@types/three": "^0.168.0",
"next": "~14.2.11",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"three": "^0.168.0"
},
As a temporary solution I am using it with try catch, build process pass but it does not work correctly, any idea on what is the problem?
let backgroundImage = null;
try {
backgroundImage = useLoader(THREE.TextureLoader, "/bg.png");
} catch (error) {}
For information, when I use it with Suspense
like explained in the documentation here, I get the Uncaught Error: document is not defined
even without build process
EDIT:
So this version looks working:
let backgroundImage = null;
try {
backgroundImage = new THREE.TextureLoader().load("/bg.png");
} catch (error) {}
Something is wrong with useLoader
? I don't really like the try catch but as a temporary solution I will keep this for the moment
TextureLoader
is requiring browser APIs that aren't available on the server (client components are still server-prerendered). I would advise you to disable SSR for the Canvas with next/dynamic
. https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-no-ssr
As an additional note, try to keep hooks within the Canvas, so instead:
// MyCanvas.jsx
import { Canvas, useLoader } from "@react-three/fiber";
import * as THREE from "three";
function Background() {
const texture = useLoader(THREE.TextureLoader, "/bg.png");
return <primitive object={texture} attach="background" />; // either this or write scene.background
}
export default function Page() {
return (
<Canvas>
<Background />
{/* blabla */}
</Canvas>
);
}
'use client';
import dynamic from 'next/dynamic';
const Page = dynamic(() => import('./Page'), { ssr: false });