Back to all notes
Next.js
3 days ago

Pin a view-transition-name so it survives the prod build

React's <ViewTransition> generates a different name in dev and prod builds, so a freeze that matched in dev missed in prod. A raw inline view-transition-name is identical in both.

The symptom

I pinned the nav bar so it stays put while the page slides underneath it during a view transition. It worked perfectly in bun dev. Deployed to production, the nav cross-faded on every navigation — the exact thing the pin was meant to prevent.

The cause

The nav was wrapped in React's <ViewTransition>, which generates the underlying view-transition-name for you. The catch: the generated name is not the same string in the development and production React builds. My freeze CSS in globals.css targeted the dev name, so in production it matched nothing and the freeze silently did nothing.

The fix

Drop React's wrapper for this one element and set a raw name yourself:

.site-nav-container {
  view-transition-name: site-nav;
}

A hand-written name is identical in dev and prod, so the CSS that freezes it matches in both.

Freezing the group

With a stable name the freeze is ordinary CSS: kill the animation, show the new snapshot, and hide the old one so two identical snapshots do not cross-fade into a dip.

::view-transition-group(site-nav) { animation: none; }
::view-transition-new(site-nav) { animation: none; opacity: 1; }
::view-transition-old(site-nav) { animation: none; opacity: 0; }

The lesson

Anything a framework generates can differ between builds — names, hashes, class strings. If you are going to match it from the outside in CSS, own the string. "Works in dev" and "works in prod" are different claims, and view transitions are exactly the kind of progressive enhancement that only bites once it is deployed.

#view-transitions#next.js#css
Found this useful?

Related notes