Why navigation gets slow after code splitting

JavaScript is often the costliest resource a web app ships, and slow load times hurt both UX and conversion rates. Code splitting is a proven remedy: it breaks the bundle into smaller chunks so the initial page doesn't download everything at once.

Slow web apps are stressful.

Two common strategies are component-level and route-level splitting. Component-level splitting isolates individual components into separate chunks, loaded on demand when an event triggers. Route-level splitting moves an entire route into its own chunk—but that means every navigation to a new route requires downloading and bootstrapping the new page from scratch, which can stall the experience on slower connections.

Smart prefetching with Guess.js

Prefetching lets the browser download and cache resources before they're needed, usually via <link rel="prefetch">. The naive approach has two flaws: it can overfetch, burning bandwidth on assets users never request, or underfetch, missing the sheet that the user actually touches.

Predictive prefetching addresses both by grounding prefetch decisions in real navigation data. Guess.js implements this by consuming a report from Google Analytics or another analytics provider and building a model of which pages a user is likely to visit next from a given starting page. The library ships with integrations for Angular, Next.js, Nuxt.js, and Gatsby.

To enable it, add your Google Analytics view ID in the webpack configuration:

const { GuessPlugin } = require('guess-webpack');

// ...
plugins: [
   // ...
   new GuessPlugin({ GA: 'XXXXXX' })
]
// ...

If you're not on Google Analytics, a custom reportProvider can feed data from any service you prefer.

The internals: from analytics to prefetch hints

Guess.js runs through a short pipeline to deliver its predictions:

  1. It extracts user navigation patterns from your chosen analytics provider.
  2. It maps the URLs in the report to the JavaScript chunks emitted by webpack.
  3. It builds a lightweight predictive model for which pages users likely land on next from any current page.
  4. It runs that model per chunk, predicting which chunks are most likely needed afterward.
  5. It writes prefetching instructions into each chunk.

The resulting output resembles a declarative roadmap for the browser:

__GUESS__.p(
  ['a.js', 0.2],
  ['b.js', 0.8]
)

That generated code tells the browser to consider chunk a.js with a probability of 0.2 and chunk b.js with a probability of 0.8. When the browser executes the instructions, Guess.js checks the user's connection speed. Over a fast connection, it inserts <link rel="prefetch"> tags for both chunks; on a weak connection, it limits prefetching to the high-probability chunk b.js.

Where to go next