Case Studies Building a 3D portfolio with Next.js and Three.js

Building a 3D portfolio with Next.js and Three.js

2 min read

Building a 3D portfolio with Next.js and Three.js
TL;DR This site pairs an interactive 3D character with a Next.js App Router build. The hard part was keeping a WebGL-heavy page fast and indexable: server-render the real content, load the 3D lazily, compress the model, and let Google see HTML while visitors get the scene.

This site is the portfolio you are reading now: an interactive 3D character built with Three.js, scroll-driven animation with GSAP, and a Next.js App Router build underneath. It looks like a toy, but the engineering question behind it was serious: how do you ship a WebGL-heavy page that is still fast and still indexable?

The problem with 3D sites

The usual failure mode of a 3D website is that everything lives inside the canvas. It looks great and ranks nowhere, because search engines see an empty page with a script tag. On top of that, WebGL and large models are heavy, so the page feels slow before the scene even appears.

I wanted the 3D and the fundamentals both, not one at the cost of the other.

The approach

The guiding rule was to separate the content from the spectacle.

  • Server-render the real content. The text, links, and structured data are normal HTML in the initial response. Google gets a full, crawlable page whether or not the WebGL runs.
  • Layer the 3D on top. The interactive character is an enhancement mounted on the client, in its own boundary, so it never blocks the content from rendering.
  • Keep it light. The model is compressed, the heavy 3D code loads lazily, and only the critical assets are preloaded. The page becomes usable before the scene is ready.

What shipped

The result is a page that reads as a proper portfolio to a crawler and as an interactive scene to a visitor. Metadata, Open Graph, and JSON-LD are server-rendered. The 3D character, the scroll animation, and the smooth scrolling all load after the content is in place. Security headers, caching, and compression are handled at the framework level.

The blog you are reading was added the same way: plain, fast, indexable pages that live alongside the 3D homepage without dragging its weight onto every route.

What I learned building it

The lesson was that "impressive" and "fast" are not opposites, but you have to decide which one loads first. Content and structure go in the initial HTML; the spectacle comes after. Every time I was tempted to make the 3D central to the page, the fix was to demote it to an enhancement and let the fundamentals lead.

If you want the interaction side to stay smooth under all that, see my notes on fixing INP. The 3D is the part people remember; the boring parts are why it ranks.

FAQ

Does a 3D website hurt SEO?

It can if the content only exists inside the canvas. The fix is to server-render the real text and structured data as normal HTML, and treat the 3D as an enhancement layered on top. Google indexes the HTML; visitors get the scene.

How do you keep a Three.js page fast?

Load the 3D code and assets lazily so they do not block the first paint, compress the model with DRACO, preload only the critical assets, and keep the main thread free so interactions stay responsive. The page should be usable before the scene finishes.

Why Next.js for a 3D site?

The App Router gives server-rendered HTML for SEO, easy per-route metadata and structured data, and a clean way to split the heavy client-only 3D into its own boundary. You get the crawlable shell and the interactive scene without fighting the framework.