The same scene on fiber 9
components/spinning-box.tsx
"use client";
import { useRef, useState } from "react";
import type { Mesh } from "three";
import { useFrame, type ThreeElements } from "@react-three/fiber";
// v8: import { MeshProps } from "@react-three/fiber". v9 removed it; use ThreeElements["mesh"].
type Props = ThreeElements["mesh"] & { speed?: number };
export function SpinningBox({ speed = 1, ...props }: Props) {
const mesh = useRef<Mesh>(null);
const [hovered, setHovered] = useState(false);
// Mutate in useFrame; don't set state every frame.
useFrame((_, delta) => {
if (mesh.current) mesh.current.rotation.y += delta * speed;
});
return (
<mesh {...props} ref={mesh} onPointerOver={() => setHovered(true)} onPointerOut={() => setHovered(false)}>
<boxGeometry args={[1.4, 1.4, 1.4]} />
<meshStandardMaterial color={hovered ? "#C6FF3D" : "#8B6CFF"} />
</mesh>
);
}
components/scene.tsx
"use client";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import { SpinningBox } from "@/components/spinning-box";
export default function Scene() {
return (
<Canvas camera={{ position: [0, 0, 5], fov: 45 }} dpr={[1, 2]}>
<ambientLight intensity={0.6} />
<directionalLight position={[3, 4, 5]} intensity={1.2} />
<SpinningBox position={[-1.2, 0, 0]} />
<SpinningBox position={[1.2, 0, 0]} speed={-0.6} />
<OrbitControls enableZoom={false} />
</Canvas>
);
}
components/scene-loader.tsx
"use client";
import dynamic from "next/dynamic";
// ssr: false keeps three.js and WebGL out of the server render.
// In Next.js 15, ssr: false is only allowed inside a client component like this one.
const Scene = dynamic(() => import("@/components/scene"), {
ssr: false,
loading: () => <div className="h-full w-full" />,
});
export function SceneLoader() {
return (
<div className="h-[420px] w-full">
<Scene />
</div>
);
}
app/r3f/page.tsx
import { SceneLoader } from "@/components/scene-loader";
export default function Page() {
return (
<main className="mx-auto max-w-4xl p-6">
<SceneLoader />
</main>
);
}