February 2, 2026
4 min read

Dissecting Vercel’s React Best Practices (Part 2): Bundle Size Optimizations

Hanzalah WaheedSoftware Engineer · Execution-First
Dissecting Vercel’s React Best Practices (Part 2): Bundle Size Optimizations

What is a bundle?

Bundles are essentially just HTML, CSS, and mostly JS files sent by the server to your browser when you open a website.

We write code in TypeScript, use a bunch of libraries (node_modules), and maybe include some images and SVGs. When we build our code, it all gets bundled into plain JavaScript files with the help of bundler tools like Webpack, Turbopack, etc.

Why should bundles be small?

Clearly, bundles are important for optimization. Let’s look at the impact of bundles across a user’s experience:

  1. A big bundle means more bytes to download. This gets worse on slow internet (3G/4G).

  2. Even after download, a large bundle takes more time to be parsed by the device. On older devices, this becomes even more laggy.

  3. At this point, the user is frustrated and the UX sucks.

  4. Even for the developer, this increases LCP, TTI, and impacts your app’s SEO.

    • LCP (Largest Contentful Paint): The time taken for the app to become visually complete (mostly affected by CSS).
    • TTI (Time to Interactive): The time taken for your app to become clickable by the user (mostly affected by JS).
  5. Also, for the developer, you are sending more data from your servers to clients, increasing CDN and bandwidth costs.

How do you make the bundle size small?

Here are the best practices that Vercel’s skill uses:

Avoid barrel imports

Never import directly from index.ts files. Try to be selective and import only specific components.

Note: Even when you do this, depending on the library, you might still be importing the entire library. To avoid this, in Next.js at least, you can enable optimizePackageImports.

Conditional module loading

Rather than importing all libraries at the top, you can import them conditionally, only when the user actually needs them.

Bad:

ts
import { hugeFrames } from "./animation-frames"

Good:

ts
useEffect(() => {
  if (!enabled) return

  import("./animation-frames").then((m) => setFrames(m.frames))
}, [enabled])

Dynamic imports

In Next.js, you can use next/dynamic to import components lazily after the page has rendered. next/dynamic supports SSR as well.

React lazy loading + Suspense boundaries

Similar to next/dynamic, but use this only on client-side pages.

If you are confused between conditional importing, dynamic imports, and lazy loading like I was, the table below should clear things up.

ApproachWhat it isBundle size impactWhen it loadsSSR supportBest use casesAvoid when
next/dynamic()Next.js wrapper for dynamically importing components✅ Reduces initial JS by splitting into separate chunksLoads when component is rendered✅ Yes (default), or ❌ with { ssr: false }Large UI components, route-level sections, expensive widgets, optional UI (charts, editors, maps)Tiny components (too many chunks hurt performance), or critical above-the-fold UI
Conditionally importing a module (if (...) await import(...))Dynamically import any JS module at runtime based on a condition✅✅ Best for avoiding heavy libraries in the initial bundleOnly when the condition happens (user action, feature flag, environment)Usually ❌ (or tricky) if condition depends on browser APIsHeavy utilities (PDF/Markdown parsers, analytics, Monaco, chart libs), feature-flagged code, on-click logicCore-path code that always runs anyway
React.lazy() + <Suspense>React-native dynamic import for components✅ Similar splitting benefit as next/dynamicWhen component renders on the client❌ In Next App Router, Suspense is more about streaming and loading statesPure client-side apps, non-Next setups, simple lazy UI chunksNext.js SSR scenarios where you need SSR control

Pre-loading

Pre-loading is when we import heavy components based on an event like hovering over a button. This gives the user a perceived faster UX.

To actually reduce bundle size when preloading, it should be combined with a typeof window !== "undefined" check so it only runs on the client.


Dissecting Vercel’s React Best Practices (Part 2): Bundle Size Optimizations | Hanzalah Waheed