Optimizing 3D Model Rendering in Next.js by Reducing Blocking Time Using React Fiber Offscreen

Whenever we use CPU-intensive operation on the Web, it becomes sluggish and un-interactable for some time. Rendering a 3D model directly affects the initial loading time of the Web, so we can use Web Workers to load them behind the scenes.
In this article, we will optimize the loading time of our model which we built in the following article:
If you want to study Web Workers in general, you can visit the following article:
Now without further ado, let's start our tutorial!
1) Install React Three Fiber:
To render your model in Web Worker you can install the following dependency using:
npm install @react-three/offscreen
2) Moving all the Children of Canvas into Another File:
In the previous “Animated Avatar” tutorial we had our model rendered something like this:
// components/avatar-renderer.tsx
"use client";
import { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { Environment, OrbitControls } from "@react-three/drei";
import Avatar from "@/Avatar";
const ModelRenderer = () => {
return (
<div className="w-full h-auto aspect-[24/25]">
<Suspense
fallback={
<div className="w-full flex items-center justify-center h-[calc(100vh-300px)] font-bold text-[30px] font-mono text-white">
loading...
</div>
}
>
<Canvas
shadows="basic"
// adjust camera parameters according to your needs
camera={{
position: [0, -5, 1.5],
fov: 50,
castShadow: true,
zoom: 1.3,
}}
className="w-full h-full"
>
<OrbitControls />
<ambientLight intensity={1.5} />
<Environment preset="sunset" />
<directionalLight intensity={0.8} />
<Avatar />
</Canvas>
</Suspense>
</div>
);
};
export default ModelRenderer;
Now we will divide this into different files.
First, we will create scence.tsx
which will contain our scene as follows:
// scene.tsx
"use client";
import Avatar from "@/Avatar";
import { Environment, OrbitControls } from "@react-three/drei";
const Scene = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={1.5} />
<Environment preset="sunset" />
<directionalLight intensity={0.8} />
<Avatar />
</>
);
};
export default Scene;
Now we will create a worker named worker.tsx
which will import the scene as follows:
// worker.tsx
"use client";
import { render } from "@react-three/offscreen";
import Scene from "./scene";
render(<Scene />);
Now, in the model-renderer.tsx
, rather than importing Canvas
from @react-three/fiber
now we will import from @react-three/offscreen
and configure our components as follows:
// components/avatar-renderer.tsx
"use client";
import { Suspense, lazy } from "react";
import { Canvas } from "@react-three/offscreen";
const Scene = lazy(() => import("./scene"));
const worker = new Worker(new URL("./worker.tsx", import.meta.url), {
type: "module",
});
const ModelRenderer = () => {
return (
<div className="w-full h-auto aspect-[24/25]">
<Suspense
fallback={
<div className="w-full flex items-center justify-center h-[calc(100vh-300px)] font-bold text-[30px] font-mono text-white">
loading...
</div>
}
>
<Canvas
worker={worker}
fallback={<Scene />}
shadows="basic"
camera={{
position: [0, -5, 1.5],
fov: 50,
castShadow: true,
zoom: 1.3,
}}
className="w-full h-full"
/>
</Suspense>
</div>
);
};
export default ModelRenderer;
Make sure to import ModelRenderer
dynamically as follows:
import dynamic from "next/dynamic";
const ModelRenderer = dynamic(() => import("@/components/model-renderer"), {
ssr: false,
});
const Page = () => {
return <>
{/* ... any other code */}
<ModelRenderer />
</>
}
export default Page;
Testing Blocking Time:
Now we will test for blocking time. The rendered model without the Web Worker is deployed at: https://next-with-animated-avatar.vercel.app/, and it gave the following results:

The model rendered using Web Worker can be found at https://next-with-animated-avatar-offload.vercel.app/. It gave the following results:

And that’s it, Happy Coding! 😊
Access the Code at:
Visit the Live version at: https://next-with-animated-avatar-offload.vercel.app/
If you liked my story you can follow me for more interesting tips and tricks at Farasat Ali — Medium.
You can Find Me on LinkedIn and GitHub.
Also, Visit My Portfolio at:
You can also explore the following related articles:
Stackademic 🎓
Thank you for reading until the end. Before you go:
- Please consider clapping and following the writer! 👏
- Follow us X | LinkedIn | YouTube | Discord
- Visit our other platforms: In Plain English | CoFeed | Venture | Cubed
- More content at Stackademic.com