Code Splitting

Method

Sending only the code a screen needs and delivering the rest when the user gets there, used to shorten how long the first screen takes to appear.

Why it exists

Websites used to keep their JavaScript separate for each page. JavaScript is the language that produces browser behavior — what opens when a button is pressed, whether an entered value passes its check. Moving between pages meant fetching a whole new document, so the code you received was only that page's.

Single-page applications (SPA) inverted that. An SPA loads one document and swaps screens inside it, which removes the flash between pages. In exchange, that single document carries the code for every screen. In a service with thirty screens, someone who only wants to sign in also downloads the admin settings, the statistics charts, and the billing history.

Pagedeck hit this directly. The first-load file grew with every feature, and at some point users on a subway connection were staring at a blank screen for more than four seconds. When Theo looked inside, half of it was a charting library used only when someone embeds a graph in a document — a feature three percent of users touch, holding up the first screen for everyone. Code splitting targets exactly that. It doesn't reduce the total; it divides when each part arrives.

How to run it

Start by measuring what's big. A bundler packs hundreds of source files into a few the browser can fetch efficiently, and most of them ship a view showing how many kilobytes each piece of code occupies inside the result. Look at that before splitting anything by instinct.

  1. Inspect what the first-load bundle is made of. The top three or four items usually account for more than half of it.
  2. Choose where to cut. Routes are the default line: entering the settings screen fetches the settings code. The fetch overlaps with the moment the user is already changing screens, so the wait registers as less of an interruption.
  3. Convert the import at that boundary into a dynamic one. The work is moving something loaded at the top of the file into the function that actually needs it, and the bundler reads that marker and emits a separate file.
  4. Decide what to show while the piece is on its way. A screen that goes blank makes people wonder whether the click registered. Use a placeholder frame or a progress indicator.
  5. Decide what happens when the fetch fails. Right after a deploy, a browser still holding old filenames can't find the piece. Add a retry or a prompt to reload so the screen doesn't simply stop.
  6. Measure again. Check whether the first load dropped from 900 kilobytes to 320, and whether largest contentful paint (LCP) came down from 4.1 seconds to 2.3. LCP is the time until the largest element on screen is painted, which is the number closest to the user's first impression.

When it fits and when it doesn't

It fits best where there are many screens and each user visits only some of them. Pagedeck is typical: nearly all the time goes to the document editor, while settings, billing, and admin screens open occasionally. Large but rarely used features are good candidates too. A charting library or an image editor can sit behind the moment someone turns that feature on, and the user who never turns it on never pays for it at all.

It does nothing when the bundle is already small. Splitting 200 kilobytes into four pieces lightens the first load a little, but it adds three requests, and those round trips eat most of what was saved. Splitting off a screen almost everyone goes to next has the same shape — code everyone downloads anyway, now arriving in two trips instead of one.

There are two limits. The first is that finer slicing means more requests. Sixty chunks can turn one screen into twelve files fetched in order, and since each one has to arrive before the next is requested, the waits stack up in a line. The second is the empty moment on every navigation. Handing back 0.4 seconds per screen change to save two seconds at the start can feel worse, not better. The usual answer is prefetching: fetch the chunk for the screen the user is most likely to open next, triggered on something like a hover over the menu, so the code is already there when the click comes.

How it sounds at work

"The whole charting library is in the first load. Let's split it out so only people using graphs fetch it."

Everyone is downloading code that a small minority uses, so cut that piece away.

"We already split by route. Going finer would just add requests."

The gain from further splitting is smaller than the round-trip cost it adds.

"Splitting is fine, but decide what we show during the load first."

Once code is split the wait becomes visible, so design what occupies that moment before shipping it.

At Pagedeck this work is the first step in any web performance conversation. When Mia brings in a complaint that the first screen is slow, Theo separates the causes into server time and download size, and if size is the problem he starts with the largest chunk.

What Mia watches isn't the kilobytes saved but the order the user experiences. If the first screen got faster while opening a document now produces a new half-second blank, that's a move rather than an improvement. Their conversations usually end by deciding which screen's waiting moves to which other screen.

What people get wrong

The most common misreading is that the total amount of code downloaded goes down. The first-load figure falls from 900 to 320, so it looks that way. What falls is only the amount delivered in the first trip; a user who visits every screen still ends up with the full 900 kilobytes. Reducing the total is what tree shaking does by removing unused code, while this technique divides timing. Confusing the two produces the puzzled question of why total transfer didn't change after splitting.

Assuming that splitting always makes things faster is the other one. It gets introduced as a performance technique, so the expectation is natural. But splitting creates new costs — more requests, and a wait on every navigation — and when the original bundle was small or the chunks were cut too fine, those costs exceed the gain. Teams that split without measuring shave 0.3 seconds off the first screen, add 0.4 seconds to every screen change, and roll it back only after complaints go up. Measuring before and after under the same conditions, on the same network and the same device class, is part of the job rather than a follow-up to it.

Easily confused with

TermWhat it defersHow it differs
Lazy loadingAssets like images and videoFetches what's below the fold when it comes into view. The target is content rather than code, and since lazy loading is often the mechanism that actually delays a split chunk, the two are frequently used together
Tree shakingNothingDrops code that was imported but never used out of the build entirely. It shrinks the total, where code splitting redistributes the timing

Where to go next

  • Frontend

    see which stage of building a screen this technique belongs to

  • Bundler

    see the tool that performs the split

Related terms

Last updated 2026-09-16