useTexture in next.js has some trouble handling caching

Hi everyone,

I’m pretty new to the Drei + react-three-fiber + Next.js stack. I have a lot of experience with React Native and vanilla three.js, but I’ve never used r3f for complex work or projects, only to try out some of Drei’s super cool features.

But here we are now I need to understand r3f + Drei + Next.js all in one. :slight_smile:

The problem

When I try to re-instance an already loaded texture using useTexture, the material doesn’t appear at all. It only shows up after a hot reload caused by me trying to fix the problem.

When it happens

When I instance a mesh (or even multiple meshes) and the associated texture (two different resources from two different fetch requests), I have no trouble at all the first time. I easily instance both, waiting for the texture with useTexture and useLayoutEffect to set some attributes like flipY or colorSpace, and thanks to other suggestions, I set my TexturedMaterial element like this:

function TexturedMaterial({ url }: { url: string }) {
  const imageTexture = useTexture(url);

  useLayoutEffect(() => {
    if (imageTexture) {
      imageTexture.flipY = true;
      imageTexture.colorSpace = THREE.SRGBColorSpace;
    }

    return () => {
      if (imageTexture) {
        imageTexture.dispose();
      }
    };
  }, [imageTexture]);

  return (
    <meshBasicMaterial
      map={imageTexture}
      toneMapped={false}
      onUpdate={(self) => {
        self.needsUpdate = true;
      }}
    />
  );
}

Everything works like a charm. This component is wrapped like this:

<OrientedAndResizedPlane
  objectDirection={objectDirection.current}
  objectSize={objectSize.current}
>
  <ErrorBoundary
    fallback={<meshStandardMaterial color={0xff0000} />}
    onError={(error, info) => {
      // Optionally trigger a toast notification here
      console.log(`Error while loading texture: ${imageUrl}`);
      toast.error(`Error while loading texture: ${imageUrl}`, {
        richColors: false,
      });
    }}
  >
    <Suspense fallback={<meshStandardMaterial />}>
      <TexturedMaterial url={imageUrl} />
    </Suspense>
  </ErrorBoundary>
</OrientedAndResizedPlane>

But if I change elements in the scene, discarding the old ones and downloading the new ones, when I try to upload textures that I already cached with Next.js, I can’t make them appear in the scene.

I’ve tried everything ChatGPT and AI friends have suggested—removing the dispose, using a custom loading function with TextureLoader from Three.js, using state to force needsUpdate in both material and texture…

I’m writing this hoping someone has had the same trouble with Next.js and Drei and can help me understand what I’m missing. I know it’s a concurrency problem, a stack race handled incorrectly, but I can’t figure out where.

Thanks