Single Page Applications (SPAs) and the False Sense of Speed: How to Audit Real Bottlenecks

Why SPAs look fast visually but fail Core Web Vitals metrics. How to investigate and resolve INP and LCP bottlenecks in Client-Side Rendering architectures.

Executive brief

Key takeaways

  • First Input Delay (FID) was replaced by INP in 2024, exposing re-rendering bottlenecks and long tasks on the main thread in SPAs.
  • Relying exclusively on Client-Side Rendering delays LCP, requiring partial SSR, bundle optimization, and preloading.
  • Lab metrics aren't enough; measuring 'soft navigations' requires instrumentation with PerformanceObserver in the field.

The critical decision every frontend engineering team needs to make when dealing with a Single Page Application (SPA) is: continue trying to optimize the overhead of Client-Side Rendering (CSR), or initiate a partial refactoring towards Server-Side Rendering (SSR). This choice directly defines the product's scalability and the stability of its metrics.

An SPA creates an illusion of speed. After the initial load, navigating between routes ("soft navigations") feels instantaneous because the browser does not re-download the complete HTML. However, the cost of this approach is shifting the interface assembly to the user's device (the main thread), which frequently destroys real Core Web Vitals metrics.

Based on Real User Monitoring (RUM) field data and Chrome User Experience Report (CrUX) performance specifications, this article investigates where hidden bottlenecks in SPAs come from and presents a clear method to audit them.

The Invisible Delay of Largest Contentful Paint (LCP)

The most common issue observed in pure SPAs is degraded LCP, even when the initial perceived loading seems fast.

This happens because the page rendering relies on a sequential chain that blocks the display of meaningful content. Evidence shows the browser needs to:

  1. Download the initial HTML (usually an empty file with a <div id="app"> container).
  2. Download giant JavaScript bundles.
  3. Parse and execute the JavaScript (blocking the main thread).
  4. Make API calls to fetch business data.
  5. Render the largest visible element on screen (the LCP).

Inference: If your LCP is above 2.5 seconds, the bottleneck is likely not the hero image's size, but the structural delay caused by waiting for JavaScript execution and data fetching.

INP and Interaction Latency in SPAs

With the adoption of Interaction to Next Paint (INP) as the official Core Web Vitals metric replacing First Input Delay (FID), the processing cost of SPAs has become even more evident.

INP tracks the latency of all interactions throughout the page's lifespan. In complex React, Vue, or Angular SPAs, a simple click often triggers long state update chains, Virtual DOM reconciliation, and the mounting of new components.

During the infamous Long Tasks (tasks blocking the main thread for over 50ms), any user input gets queued up. If a click to open a modal or expand an accordion takes more than 200ms to visually reflect on screen, the SPA will fail the INP assessment.

Lab Limitations and "Soft Navigations"

A major limitation of synthetic auditing tools, such as the traditional Lighthouse, is that they primarily capture full page loads (hard navigations).

In an SPA, most navigation happens via the browser's History API, dynamically altering the DOM (soft navigations). The Chrome metrics API is still adapting the collection of LCP and other metrics during these transitions, meaning your lab tests might be green, but the user's field experience (CrUX data) shows severe delays.

Therefore, relying solely on lab tools to audit an SPA guarantees a false positive in performance reporting.

Action Plan to Audit Real Bottlenecks

The technical recommendation for teams operating SPAs requires proper RUM instrumentation and architectural changes.

1. Properly Instrument RUM Implement the web-vitals.js library to monitor field interactions, and enable tools that support the new PerformanceObserver API capable of flagging soft navigations. Recommended action: Audit your data collection pipeline and validate if click and scroll events are properly logging INP data.

2. Break Down "Long Tasks" (Code Splitting) Replace sending a giant monolithic bundle with Route-Based Code Splitting techniques. Ensure the user downloads only the code for the current screen. Use [React](/blog/technical-seo-audit-nextjs).lazy (or your framework's equivalent) to defer large chunks. Verification: Open Chrome DevTools, navigate to the Performance tab, and identify red blocks indicating tasks longer than 50ms. The goal is to eliminate them from the initial load.

3. Shift CSR Weight to SSR/SSG Evaluate migrating critical paths (like Landing Pages and Checkout flows) to meta-frameworks such as Next.js, Nuxt, or Remix, utilizing SSR or Static Site Generation (SSG). Impact: This delivers the LCP content directly within the initial HTML, drastically reducing blocking request chains.

Auditing an SPA requires looking past simplified reports. By basing decisions on real evidence from the main thread, you can convert the false sense of speed into consistent, Core Web Vitals-approved performance.

Direct answers

Frequently asked questions

Why is my SPA's LCP so high if the screen loads quickly?

The initial screen loads a 'shell' quickly, but the largest content element (LCP) is only rendered after downloading, parsing, and executing JavaScript, plus fetching API data.

How does INP affect SPA development?

INP measures all interactions throughout the page's lifecycle. Since SPAs delegate state updates and rendering to JavaScript, heavy updates on the main thread delay the next paint, harming INP.

Should I abandon the SPA model to achieve good performance?

Not necessarily. You can adopt hybrid patterns like Server-Side Rendering (SSR) and Static Site Generation (SSG) for initial loads, and use chunks and lazy-loading to hydrate the application progressively.

Was this helpful?Leave your feedback to help us improve.