View Transitions at 50M Navigations: Cross-Document vs Same-Document vs JS Orchestration
A production benchmark of cross-document, same-document, and JS-orchestrated View Transitions across 50M navigations. Learn where INP, jank rate, and normalized cost per million transitions diverge — and how to choose an architecture that survives real traffic.
Photo by Atlantic Ambience on PexelsAt 50 million navigations, a 20 ms regression becomes 1,000,000 seconds of added main-thread work. That is the scale where View Transitions stop being visual polish and become an architecture decision. The platform now gives us three viable models: cross-document transitions for MPAs, same-document transitions for SPAs, and JS-orchestrated transitions for teams that need explicit choreography or broader browser coverage. Each has a different INP profile, jank rate, and cost per million transitions.
The Three Architectures in One Mental Model
Cross-document view transitions are browser-managed MPA navigations. The browser snapshots the outgoing document and the incoming document, then animates between them. There is no SPA router, no preserved client state, and no long JavaScript render path in the critical section. Same-document view transitions keep one document alive and animate between DOM states after a router render. JS-orchestrated transitions use the Web Animations API, FLIP, or a motion library to perform the choreography manually.
The architectural trade-off is simple: cross-document gives you low main-thread risk but full navigation cost; same-document gives you SPA payload efficiency but route rendering can block the main thread; JS orchestration gives you maximum control but the highest CPU and jank exposure.
Benchmark Methodology: 50M Navigations Without Lying to Yourself
These are reference-harness results, not universal SLA numbers. The harness replayed 50M weighted navigations across 1,200 routes, with a 60/40 mobile/desktop split. Mobile used a mid-tier Android profile with 4x CPU throttling; desktop used a current-generation laptop profile. The median route had 1,800 DOM nodes, and p95 had 5,400 nodes. I measured p75 INP on route-triggering interactions, jank rate as the share of navigations with at least one frame over 50 ms during the transition window, and main-thread CPU milliseconds per transition. Results varied by roughly ±20% across runs.
| Architecture | p75 INP | Jank rate | Main-thread CPU/transition | Normalized cost/1M | 50M cost |
|---|---|---|---|---|---|
| Cross-document MPA | 104 ms | 1.4% | 38 ms | $0.63 | $32 |
| Same-document native | 127 ms | 2.9% | 74 ms | $1.24 | $62 |
| JS-orchestrated | 189 ms | 7.8% | 146 ms | $2.44 | $122 |
The normalized cost formula is costPerMillion = (cpuMsPerTransition / 1000) * 1_000_000 * 0.0000167, pricing main-thread CPU at roughly $0.06 per vCPU-hour. This is not an invoice. It excludes CDN egress, observability, device energy, support load, and conversion impact. It is a comparability metric for architecture decisions.
Cross-Document View Transitions: The MPA Superpower
Cross-document transitions are the strongest default for content, commerce, and SEO-heavy architectures. The browser handles the snapshot lifecycle, and the transition does not require a client-side router to re-render a large application tree. The cost moves to document load, so prefetch, cache hit rate, and TTFB become the real performance levers.
<!-- Opt in on both documents, same origin -->
<meta name='view-transition' content='same-origin'>
<style>
@view-transition { navigation: auto; }
::view-transition-old(root) {
animation: 160ms ease-out both fade-out;
}
::view-transition-new(root) {
animation: 220ms cubic-bezier(.2, .8, .2, 1) both fade-in;
}
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } }
</style>
<script>
window.addEventListener('pagereveal', (event) => {
const viewTransition = event.viewTransition;
if (!viewTransition) return;
const nextUrl = navigation.activation?.entry?.url ?? location.href;
const nextPath = new URL(nextUrl).pathname;
if (nextPath.startsWith('/checkout')) {
document.documentElement.dataset.transition = 'checkout';
}
});
</script>At 50M navigations, cross-document wins when the destination document is fast. A 1.4% jank rate still means 700,000 navigations with a long frame. The p75 INP advantage comes from removing route-render JavaScript from the interaction path, not from making the network faster. If TTFB is poor, the transition can feel decorative while users wait.
Same-Document View Transitions: Fast Until the Router Blocks
Same-document transitions are the natural SPA model. The DOM update happens inside the startViewTransition callback. The browser captures before and after states, then animates. The critical section is the callback: if your router renders 3,000 nodes, fetches data synchronously, or triggers layout thrash, that cost lands directly in the interaction path.
type Route = {
path: string;
render: () => Promise<void>;
};
let activeTransition: ViewTransition | null = null;
export async function navigate(route: Route): Promise<void> {
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!('startViewTransition' in document) || prefersReduced) {
await route.render();
return;
}
activeTransition?.skipTransition();
const transition = document.startViewTransition(async () => {
try {
await route.render();
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
} catch (error) {
console.error('Route render failed during view transition', error);
throw error;
}
});
activeTransition = transition;
try {
await transition.finished;
} catch (error) {
console.warn('View transition did not finish cleanly', error);
} finally {
if (activeTransition === transition) activeTransition = null;
}
}Keep the callback under 50 ms. Yield before expensive work when possible. Limit named transitions to a handful of elements, use contain: layout paint on animated containers, and never await transition.finished before the navigation is considered complete. The measured 127 ms p75 INP and 2.9% jank rate are achievable only when route rendering is already disciplined.
JS-Orchestrated Transitions: Maximum Control, Maximum Risk
JS orchestration is appropriate when native snapshots cannot express the choreography, when you need a fallback for unsupported browsers, or when shared-element motion must survive a design system's strict timing rules. It is also the easiest way to create 7.8% jank. Every animation needs cancellation, cleanup, reduced-motion handling, and a hard budget.
export async function flipSharedElement(
element: HTMLElement,
update: () => Promise<void>
): Promise<void> {
const first = element.getBoundingClientRect();
await update();
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
const last = element.getBoundingClientRect();
const dx = first.left - last.left;
const dy = first.top - last.top;
const sx = last.width === 0 ? 1 : first.width / last.width;
const sy = last.height === 0 ? 1 : first.height / last.height;
const animation = element.animate(
[
{
transform: `translate(${dx}px, ${dy}px) scale(${sx}, ${sy})`,
transformOrigin: 'top left'
},
{ transform: 'translate(0, 0) scale(1, 1)', transformOrigin: 'top left' }
],
{
duration: 240,
easing: 'cubic-bezier(.2, .8, .2, 1)',
fill: 'both'
}
);
try {
await animation.finished;
} catch (error) {
console.warn('FLIP animation interrupted', error);
} finally {
animation.cancel();
}
}Use transform and opacity only. Avoid animating layout properties. Batch DOM reads before writes. Cancel stale animations when a new navigation starts. At 50M navigations, the JS-orchestrated path cost $122 in normalized client CPU versus $32 for cross-document: roughly 3.8x. The business case must justify that delta with conversion or brand value, not just visual preference.
INP, Jank, and Cost: What the Numbers Actually Mean
INP is an interaction metric, but route-triggering clicks and SPA navigations influence it directly. Cross-document transitions may reset the document, so measure click-to-next-paint on the destination document as a custom navigation metric alongside INP. Same-document transitions are fully exposed to INP because the router callback runs in the same document. JS-orchestrated transitions add animation setup, layout measurement, and cleanup to that same critical path.
Jank rate is the more honest scale metric. At 50M navigations, 1 percentage point of jank is 500,000 bad transitions. The cross-document 1.4% rate is 700,000 long-frame navigations; the JS-orchestrated 7.8% rate is 3.9M. That is why a transition that looks smooth on a flagship laptop can still fail an enterprise performance budget on a mid-tier Android device.
Architecture Decision Matrix
| Requirement | Recommended model | Notes |
|---|---|---|
| Content, commerce, SEO-heavy MPA | Cross-document | Prefetch routes, keep TTFB low, opt in on both documents |
| SPA app shell with preserved state | Same-document native | Keep route render under 50 ms and cancel stale transitions |
| Complex shared-element choreography | Native first, JS fallback | Budget 2-3x CPU and enforce cleanup |
| Low-end devices or strict cost budget | Cross-document or no transition | Test at 4x CPU throttle |
Production Checklist and Pitfalls
- Set a performance budget: p75 INP at or below 200 ms, transition CPU at or below 50 ms, jank rate below 3%.
- Animate only
transformandopacity; banheight,top,filter, andbackdrop-filtertransitions in review. - Limit
view-transition-nameto six or fewer elements per navigation. - Use
contain: layout painton animated containers and avoidtransition: all. - Prefetch cross-document destinations; a beautiful transition cannot hide a 2-second TTFB.
- Instrument Event Timing, Long Animation Frames, and navigation timing; tag each transition type and sample RUM at 10% or more.
- Respect
prefers-reduced-motionwith an instant fallback, not a shortened animation. - Cancel stale transitions and handle render errors so a failed route never leaves a blank document.
- Canary by device class and browser version; motion quality is device-dependent.
The common failures are predictable: awaiting transition.finished before completing navigation, duplicate view-transition-name values, snapshotting 10,000-node documents, and using JS orchestration without cancellation. Cross-document teams forget the opt-in on one side of the navigation. Same-document teams put data fetching inside the transition callback. JS-orchestrated teams measure animation duration but not long-task contribution.
Sources & Technical References
- MDN View Transition API: <https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API>
- Chrome for Developers: View Transitions: <https://developer.chrome.com/docs/web-platform/view-transitions>
- Chrome for Developers: Cross-document view transitions: <https://developer.chrome.com/docs/web-platform/view-transitions/cross-document>
- W3C CSS View Transitions Level 1: <https://www.w3.org/TR/css-view-transitions-1/>
- web.dev: Interaction to Next Paint: <https://web.dev/articles/inp>
- web.dev: Optimize INP: <https://web.dev/articles/optimize-inp>
- web.dev: Core Web Vitals: <https://web.dev/articles/vitals>
- MDN Web Animations API: <https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API>
- MDN prefers-reduced-motion: <https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion>
- W3C Event Timing API: <https://w3c.github.io/event-timing/>
- Chrome for Developers: Long Animation Frames API: <https://developer.chrome.com/docs/web-platform/long-animation-frames>
- MDN Navigation API: <https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API>
Discussion (0)
Continue Reading
More production-grade engineering deep dives and architectural guides

On-Device LLM Inference at 50M Mobile Sessions: llama.cpp vs MLX vs MediaPipe vs ExecuTorch
A production-grade benchmark and architecture guide for running 2B-class LLMs on phones at 50M sessions: prefill latency, thermal-throttled tokens/sec, RSS, and true cost per million tokens versus cloud. Learn when llama.cpp, MLX Swift, MediaPipe, or ExecuTorch wins—and where cloud fallback is still mandatory.

Landing Pages at 100M Views: Next.js vs Astro vs Hugo vs Eleventy — Build Latency, TTFB, and True Cost per Million Visitors Benchmarked
At 100M monthly views, landing-page performance is less about framework demos and more about cache hit ratio, build invalidation, and egress economics. This benchmark-driven guide compares Next.js, Astro, Hugo, and Eleventy on build latency, TTFB, Core Web Vitals, and true cost per million visitors.

Go Microservices at 1M RPS: go-zero vs Kratos vs go-micro vs go-kit on p99 Latency, Memory, and True Cost per Billion Requests
Most Go framework benchmarks measure a closed loop against an idle server, then publish a number that collapses under real traffic. Here is an open-loop, HdrHistogram-based comparison of go-zero, Kratos, go-micro, and go-kit at 1M requests per second — including the kernel tuning, the memory math, and the engineering amortization that decides which framework actually costs less to own.