pmndrs/react-three-fiber

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