web development
Why Your React Website Is Slow (And How to Fix It)
Is your React site failing Core Web Vitals? Here are the real reasons it is slow and exactly how to fix each one in 2025.
TL;DR: React websites slow down due to large bundles, no code splitting, unoptimised images, render-blocking resources, and missing caching. Fix them with lazy loading, dynamic imports, Next.js or Vite, image optimisation, and a CDN. Most React sites can cut load time by 50–70% with these changes.
Why Your React Website Is Slow — And How to Fix It in 2025
You built your React site. It looks great in development. Then you run PageSpeed Insights and it scores 42 on mobile.
Sound familiar?
A slow React website is not just an annoyance — it directly costs you customers. Google's own data shows that 53% of mobile users abandon a page that takes longer than 3 seconds to load. And since 2021, Core Web Vitals are a direct Google ranking signal.
This guide breaks down exactly why React sites go slow, and gives you the specific fixes — in plain English, with tool names and numbers.
The Business Case for Speed First
Before we get technical, let's be clear about what's at stake.
A 1-second delay in page load time leads to a 7% drop in conversions, according to Akamai. For a business doing £10,000/month through their website, that's £700 gone — every month — from a fixable technical problem.
Speed is not a developer vanity metric. It is a revenue metric.
Reason 1: Your JavaScript Bundle Is Too Large
This is the most common culprit. By default, React ships everything in one giant bundle.js file. The browser has to download, parse, and execute the entire file before your user sees anything.
A typical unoptimised React app ships 500KB–2MB of JavaScript. On a mid-range mobile connection, that alone takes 3–8 seconds.
The Fix: Code Splitting and Dynamic Imports
Code splitting means breaking your bundle into smaller chunks that load only when needed.
// Before: loads everything upfront
import HeavyDashboard from './HeavyDashboard';
// After: loads only when the user navigates to it
const HeavyDashboard = React.lazy(() => import('./HeavyDashboard'));
Wrap lazy-loaded components in <Suspense> with a fallback:
<Suspense fallback={<div>Loading...</div>}>
<HeavyDashboard />
</Suspense>
Tool to check your bundle: webpack-bundle-analyzer or Vite's built-in --report flag. Run it and you'll usually find 2–3 libraries responsible for 60% of your bundle size.
Reason 2: No Server-Side Rendering or Static Generation
React by default is a client-side rendered (CSR) framework. The browser receives a near-empty HTML file and then runs JavaScript to build the page. This is slow for two reasons:
- The user sees a blank screen while JS loads and executes.
- Search engine crawlers get less content to index.
The Fix: Switch to Next.js
If your site is mostly content (landing pages, blogs, marketing), Next.js with static site generation (SSG) eliminates this problem entirely. Pages are pre-rendered at build time and served as static HTML.
For dynamic content (user dashboards, real-time data), use Next.js server-side rendering (SSR) or Incremental Static Regeneration (ISR).
At dipanshudev.com/services, every project Dipanshu builds uses Next.js by default precisely because of this performance and SEO advantage. A React SPA is only recommended when the project genuinely needs it.
Benchmark: Switching a marketing site from Create React App to Next.js SSG typically improves Largest Contentful Paint (LCP) by 40–60%.
Reason 3: Unoptimised Images
Images are often the heaviest assets on any web page. An uncompressed PNG hero image can easily be 2–4MB. On mobile, that single image kills your performance score.
The Fix: Modern Formats, Correct Sizing, Lazy Loading
Follow this checklist:
- Convert to WebP or AVIF — WebP is 25–35% smaller than JPEG at the same quality. AVIF is even better but browser support is still growing.
- Serve correct dimensions — Don't serve a 2400px image in a 400px container. Use responsive
srcset. - Lazy load below-the-fold images — Add
loading="lazy"to all<img>tags not in the initial viewport. - Use Next.js
<Image>component — It handles WebP conversion, lazy loading, and responsive sizes automatically.
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority // only for above-the-fold images
/>
Reason 4: Render-Blocking Third-Party Scripts
Analytics tools, chat widgets, heatmap scripts, and ad trackers are added to websites one by one over time. Each one blocks rendering and adds latency.
A typical marketing site has 8–12 third-party scripts. Individually they seem small. Together they add 1–3 seconds of blocking time.
The Fix: Load Scripts Asynchronously and Defer Non-Critical Ones
<!-- Blocks rendering — bad -->
<script src="analytics.js"></script>
<!-- Async: loads in parallel, executes when ready — better for analytics -->
<script async src="analytics.js"></script>
<!-- Defer: loads in parallel, executes after HTML is parsed — best for most scripts -->
<script defer src="chat-widget.js"></script>
In Next.js, use the <Script> component with strategy="lazyOnload" for non-critical scripts like live chat:
import Script from 'next/script';
<Script src="https://chat-widget.js" strategy="lazyOnload" />
Reason 5: No Caching Strategy
Every time a user visits your site, their browser re-downloads all your assets — unless you tell it not to. Without proper cache headers, a returning visitor has the same load time as a first-time visitor.
The Fix: Cache-Control Headers and a CDN
Set long cache lifetimes for assets with content-hashed filenames (Vite and Next.js do this by default):
Cache-Control: public, max-age=31536000, immutable
Deploy behind a CDN (Content Delivery Network) like Vercel Edge Network, Cloudflare, or AWS CloudFront. This serves your assets from servers geographically close to your user — a user in London gets assets from a London server, not a Mumbai one.
Impact: CDN alone typically reduces Time to First Byte (TTFB) by 50–80% for international visitors.
Reason 6: Too Many Re-Renders
React re-renders components whenever state or props change. In poorly structured apps, one state change can trigger dozens of unnecessary re-renders, causing jank and sluggishness in the UI.
The Fix: Memoisation
Use React.memo, useMemo, and useCallback to prevent unnecessary re-renders:
// Prevent a child from re-rendering when parent state changes
const ExpensiveComponent = React.memo(({ data }) => {
return <div>{data}</div>;
});
// Memoize expensive calculations
const processedData = useMemo(() => heavyCalculation(rawData), [rawData]);
// Memoize callbacks passed to children
const handleClick = useCallback(() => doSomething(), []);
Use the React DevTools Profiler to identify which components are re-rendering most often.
React Performance Optimisation: Quick Reference Table
| Problem | Tool/Fix | Expected Impact |
|---|---|---|
| Large JS bundle | Code splitting + lazy loading | 30–60% smaller initial load |
| CSR blank screen | Next.js SSG/SSR | 40–60% faster LCP |
| Heavy images | WebP + Next.js Image | 25–50% smaller image payloads |
| Blocking scripts | async/defer + Script strategy | 0.5–2s faster FCP |
| No caching | CDN + Cache-Control headers | 50–80% faster TTFB |
| Excessive re-renders | React.memo + useMemo | Smoother UI, better INP |
How to Measure Your Current Performance
Before you fix anything, measure. Use these free tools:
- Google PageSpeed Insights — pagespeed.web.dev — score on real mobile + desktop
- Lighthouse — built into Chrome DevTools (F12 → Lighthouse tab)
- WebPageTest — webpagetest.org — waterfall view to see exactly what loads when
- Core Web Vitals in Search Console — shows real-user data from your actual visitors
Aim for:
- LCP (Largest Contentful Paint): under 2.5 seconds
- INP (Interaction to Next Paint): under 200ms
- CLS (Cumulative Layout Shift): under 0.1
Real Example: From 41 to 89 on PageSpeed
One of Dipanshu's clients — a SaaS landing page built in React — was scoring 41 on mobile PageSpeed. After auditing the site, the main issues were: a 1.8MB unoptimised hero video, 11 third-party scripts loading synchronously, and no code splitting on a large chart library.
After migrating to Next.js, replacing the video with an optimised WebP image, deferring all non-critical scripts, and adding dynamic imports for the chart:
- PageSpeed mobile score: 41 → 89
- LCP: 6.2s → 1.8s
- Conversion rate on the page: +34% over the following 30 days
The development work took 3 days. The business impact was immediate. See more case studies at dipanshudev.com/projects.
Step-by-Step Fix Plan
If your React site is slow right now, follow this order:
- Run PageSpeed Insights and note your LCP, INP, and CLS scores
- Run webpack-bundle-analyzer — identify your 3 largest dependencies
- Add
React.lazy()and<Suspense>to routes and heavy components - Audit your images — convert to WebP, add
loading="lazy", use correct dimensions - Check your
<head>for synchronous third-party scripts — switch toasyncordefer - Consider migrating to Next.js if you're on CRA or plain Vite and SEO matters
- Deploy behind Vercel or Cloudflare for automatic CDN and edge caching
- Re-run PageSpeed and measure the improvement
Internal Links
- See Dipanshu's services for React and Next.js development
- View real project case studies with performance results
- Get a performance audit for your existing React site
Want This for Your Website?
If your React site is slow and you want it fixed properly — with better performance, better SEO, and better conversions — let's talk.
Want this for your website? Let's talk → dipanshudev.com/contact
FAQ
Q: Why is my React app slow on mobile but fast on my laptop? A: Developer laptops are 4–8x more powerful than the average user's mobile device. Google's Lighthouse simulates a mid-range Android on a throttled connection. Always test on mobile, not just desktop.
Q: Do I need to rewrite my whole app in Next.js to fix performance? A: Not always. You can apply code splitting, image optimisation, and script deferral to any React app. But if SEO is important to you, migrating to Next.js for the marketing pages (while keeping your React SPA for the app) is worth the investment.
Q: What is LCP and why does it matter? A: LCP (Largest Contentful Paint) measures how long it takes for the main content of a page to load. It is the most user-visible of the Core Web Vitals. Google uses it as a ranking signal. Under 2.5 seconds is "Good".
Q: How do I know if my React website is hurting my Google rankings? A: Check Google Search Console → Core Web Vitals report. If you see "Poor" or "Needs Improvement" URLs, those pages are being penalised in rankings. Also check your PageSpeed score — anything under 50 on mobile is affecting rankings.
Q: Is it expensive to fix React performance issues? A: Most performance fixes are relatively small development tasks — a few hours to a few days. The ROI is usually immediate through improved rankings and conversions. A performance audit from dipanshudev.com will tell you exactly what needs fixing and how much it will cost.
