How to Fix JavaScript Issues in React / Next.js
React and Next.js apps fail performance not from server speed but from client JavaScript: too much shipped, too much execution, too much hydration. This guide covers React/Next.js JS issues. Pair with JS Checker guide.
Step-by-step: How to fix React / Next.js JS issues
- Analyse bundle size. Install @next/bundle-analyzer. Add to next.config.js. Build with ANALYZE=true npm run build. Visualises every chunk — find the biggest and ask: is it needed on this page? Common heavy chunks: moment.js (use date-fns or dayjs), lodash full vs lodash-es modular, large icon libraries shipping all icons.
- Reduce 'use client' scope. Next.js App Router: every 'use client' component ships to the browser. Mark only what needs interactivity. Layouts, page wrappers, static content sections: leave as Server Components. Pages with mostly static content + small interactive islands: massive bundle reduction.
- Code split heavy components. Charts, rich editors, video players, modal libraries: dynamic import. const Chart = dynamic(() => import('./Chart'), { ssr: false, loading: () => <Skeleton /> }). Loads only when needed.
- Fix hydration errors. React console: 'Hydration failed' warnings. Each represents a server/client render mismatch. Common: Math.random() in render, Date during render, browser API access in render. Fix by moving non-deterministic logic into useEffect.
- Optimise INP. Long tasks block interaction. Identify via DevTools Performance → record interaction → flame chart shows long tasks. Reduce: break long synchronous work into chunks (setTimeout with 0), use Web Workers for heavy compute, defer non-critical work.
- Audit third-party scripts. Next/script with strategy='lazyOnload' for non-critical (analytics, chat, marketing). strategy='beforeInteractive' only for critical (auth, framework deps). Most stores have 5-15 third-party scripts; each can cost 50-200ms.
- Add error monitoring. Sentry, LogRocket, Rollbar. Capture client errors, hydration errors, unhandled promises. Per-deploy release tracking. Real-user data far more useful than dev testing alone.
Frequently Asked Questions
Why is my Next.js bundle so large?
Common culprits: 1) Large libraries imported wholesale (lodash, moment, full icon sets). 2) Client components when Server Components would work. 3) No code splitting — everything in main bundle. 4) Polyfills for browsers you don't need to support. Use @next/bundle-analyzer to find the biggest offenders.
What's the difference between React Server Components and Client Components?
Server Components (default in App Router) run on the server, don't ship JS to client, can be async, can access server-only APIs. Client Components ('use client' directive) ship to client, can use hooks (useState, useEffect), can handle events. Default to server; opt into client only where needed.
How do I find slow React components in production?
Sentry Performance, Vercel Analytics, or the React DevTools Profiler. Profiler highlights components that re-render frequently or take long to render. In production: real-user monitoring catches what synthetic tests miss.
Best Next.js JS optimisation libraries?
@next/bundle-analyzer (built-in chunk visualisation). next/dynamic (built-in code splitting). next/script (third-party script management). For runtime perf: million.js (React drop-in replacement claiming 70% faster renders — emerging, not mainstream yet).
Why does my React app have 'unused JavaScript' in PageSpeed?
Code shipped to the browser but not executed on the current page. Caused by: lack of code splitting (everything in one bundle), client components that should be server, third-party scripts loaded everywhere not just where used. Solution: dynamic imports, RSC for static content, conditional script loading.