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
Docker multi-stage builds: cutting images by 90%
Getting rid of ENOSPC alerts and making deploys fast again with a proper Dockerfile.
BackendNestJS vs Express: why we choose a framework with architecture
The end of spaghetti backends: what dependency injection and a strict structure actually buy you.
ArchitectureMoving from a legacy monolith to services without downtime
A practical account of splitting a legacy monolith for a logistics company without a single maintenance window.
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.