Frontend

React and Next.js performance: how we made a site three times faster

20 May 2024 · 7 min read · SmartCG

The fight for Core Web Vitals: server components, dynamic imports and removing unnecessary re-renders.

The slow frontend problem

A lot of JavaScript means a long TTI (Time to Interactive). We took over a project that shipped a 2 MB bundle to mobile devices. On a mid-range Android phone the page took seconds to become usable, and conversion was falling for reasons nobody could see in analytics.

Step 1: React Server Components

We moved heavy components — carousels, article lists, filter panels — to Server Components in Next.js. The HTML is rendered on the server, and the client no longer downloads the JavaScript of libraries it does not need to interact with. For content-heavy B2B pages this is usually the single biggest win.

Step 2: dynamic imports

Heavy libraries (charting, PDF generation, rich text editors) we wrapped in next/dynamic. They now load only when the user actually reaches the block that needs them, instead of being part of the first payload of every page.

Step 3: fewer re-renders

Most of the remaining jank came from state placed too high in the tree. Moving state down to the component that owns it, memoising expensive lists and splitting context by concern removed work that React was doing on every keystroke.

Results

  • LCP (Largest Contentful Paint) went from 4.5s to 1.2s.
  • Google PageSpeed score went from 45 to 95.
  • The bundle stopped growing every sprint, because new features had a default pattern to follow.

Services involved

Read next

Site feels slow and the team is out of ideas?

We measure what actually blocks the main scenario, then fix the causes — bundle, rendering, images, caching — instead of adding another plugin.