I know this specific error has been asked here multiple times but i think this situation is unique enough to deserve its own post.
So i Just started thinkering with three.js in nextjs 14.
Everything seems to work perfectly locally but i still see the following error:
ReferenceError: document is not defined
I think there’s a point in here somewhere that uses the document to early to create the canvas i think but i haven’t located it yet as i suppose it will be in the threejs code itself.
Anyone here done something similar like this and knows where this issue get triggered?
"use client";
export const dynamic = "auto";
import { ISSPosition, fetchISSPosition } from "@/lib/api";
import { convertCoordsToSphere } from "@/lib/functions";
import React, { useRef, useEffect, useState } from "react";
import * as THREE from "three";
const earthRadius = 10;
const scene = new THREE.Scene();
// Fog setup
...
// Camera and renderer setup
...
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
});
// Lighting setup
...
// Earth model setup
const geometry = new THREE.SphereGeometry(earthRadius, 64, 64);
const earthMat = new THREE.MeshPhongMaterial({
shininess: 350,
map: new THREE.TextureLoader().load("/earth_texture.jpg"),
bumpMap: new THREE.TextureLoader().load("/earth_texture.jpg"),
});
const earth = new THREE.Mesh(geometry, earthMat);
earth.receiveShadow = true;
earth.rotation.x = 0.3;
scene.add(earth); // Add Earth to the scene upfront
// ISS model setup
...
const animate = () => {
requestAnimationFrame(animate);
earth.rotation.y -= 0.001;
renderer?.render(scene, camera);
};
const ThreeScene = () => {
const [issPosition, setIssPosition] = useState<ISSPosition>();
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const fetchISSData = async () => {
setIssPosition(await fetchISSPosition());
};
const intervalId = setInterval(fetchISSData, 5000);
if (typeof window !== "undefined") {
containerRef.current?.appendChild(renderer.domElement);
}
animate();
const handleResize = () => {
if (renderer) {
let size =
window.innerWidth * 0.8 > 800 ? 800 : window.innerWidth * 0.8;
renderer.setSize(size, size);
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
clearInterval(intervalId);
};
}, []);
useEffect(() => {
const animate = () => {
requestAnimationFrame(animate);
if (issPosition) {
const pointCoords = convertCoordsToSphere(
Number(issPosition.iss_position.latitude),
-Number(issPosition.iss_position.longitude),
earthRadius
);
issPoint.position.copy(pointCoords.applyMatrix4(earth.matrix));
scene.add(issPoint);
}
renderer.render(scene, camera);
};
animate();
}, [issPosition]);
return (
typeof window !== "undefined" && (
<>
<div className="m-auto max-w-fit" ref={containerRef}></div>
{issPosition ? (
<div className="flex flex-col gap-2 mb-8 w-fit justify-center items-center mx-auto">
<span>Latitude: {issPosition?.iss_position.latitude}</span>
<span>Longitude: {issPosition?.iss_position.longitude}</span>
</div>
) : (
<div className="flex gap-4 justify-center">
<p className="mb-4">Locating ISS</p>
<i className="border-b- border-primary w-4 h-4 rounded-full animate-spin"></i>
</div>
)}
</>
)
);
};
export default ThreeScene;