Back to all posts
ReactPerformanceOptimization

React Performance Optimization

Nov 22, 2025
15 min read

React Performance Optimization

Performance isn't about premature optimization. It's about building systems that stay fast as they grow.

When to Optimize

Don't optimize everything. Optimize when:

  • Users report slowness
  • Metrics show issues (Core Web Vitals)
  • Profiling reveals bottlenecks
  • You're building data-heavy features

Measure first, optimize second.

Profiling Tools

Use the right tools:

  1. React DevTools Profiler - See component render times
  2. Chrome Performance - Analyze full page performance
  3. Web Vitals - Track real user metrics
  4. Lighthouse - Automated audits

Common Performance Issues

1. Unnecessary Re-renders

Problem: Component re-renders when props haven't changed.

// Bad: Creates new object every render function Parent() { return <Child config={{ theme: "dark" }} />; } // Good: Stable reference const CONFIG = { theme: "dark" }; function Parent() { return <Child config={CONFIG} />; } // Or use useMemo for dynamic values function Parent() { const config = useMemo(() => ({ theme: "dark" }), []); return <Child config={config} />; }

Use React.memo wisely:

const ExpensiveComponent = React.memo(({ data }) => { // Heavy rendering logic return <div>{/* ... */}</div>; });

2. Large Lists Without Virtualization

Problem: Rendering 10,000 rows kills performance.

Solution: Use virtualization.

import { useVirtualizer } from "@tanstack/react-virtual"; function VirtualList({ items }) { const parentRef = useRef<HTMLDivElement>(null); const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50, }); return ( <div ref={parentRef} style={{ height: "500px", overflow: "auto" }}> <div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative", }} > {virtualizer.getVirtualItems().map((virtualRow) => ( <div key={virtualRow.index} style={{ position: "absolute", top: 0, left: 0, width: "100%", height: `${virtualRow.size}px`, transform: `translateY(${virtualRow.start}px)`, }} > {items[virtualRow.index].name} </div> ))} </div> </div> ); }

Only renders visible items. Huge performance win.

3. Heavy Computations in Render

Problem: Expensive calculations on every render.

// Bad: Recalculates every render function DataTable({ data }) { const sorted = data.sort((a, b) => a.value - b.value); const filtered = sorted.filter(item => item.active); return <Table data={filtered} />; } // Good: Memoized function DataTable({ data }) { const processed = useMemo(() => { const sorted = [...data].sort((a, b) => a.value - b.value); return sorted.filter(item => item.active); }, [data]); return <Table data={processed} />; }

4. Inline Functions as Props

Problem: New function every render = child re-renders.

// Bad function Parent() { return ( <Child onClick={() => console.log("clicked")} /> ); } // Good function Parent() { const handleClick = useCallback(() => { console.log("clicked"); }, []); return <Child onClick={handleClick} />; }

5. Context Provider Re-renders

Problem: Context value changes = all consumers re-render.

// Bad: New object every render function ThemeProvider({ children }) { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); } // Good: Memoized value function ThemeProvider({ children }) { const [theme, setTheme] = useState("light"); const value = useMemo(() => ({ theme, setTheme }), [theme]); return ( <ThemeContext.Provider value={value}> {children} </ThemeContext.Provider> ); }

Code Splitting

Load code only when needed:

import { lazy, Suspense } from "react"; const Dashboard = lazy(() => import("./Dashboard")); const Settings = lazy(() => import("./Settings")); function App() { return ( <Suspense fallback={<Loading />}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/settings" element={<Settings />} /> </Routes> </Suspense> ); }

Image Optimization

Images are usually the biggest assets:

// Use modern formats <img src="hero.webp" srcSet="hero-sm.webp 400w, hero-md.webp 800w, hero-lg.webp 1200w" sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1200px" alt="Hero" loading="lazy" />

Use loading="lazy" for below-the-fold images.

State Management Performance

Use Zustand for Better Performance

import { create } from "zustand"; const useStore = create((set) => ({ todos: [], addTodo: (todo) => set((state) => ({ todos: [...state.todos, todo] })), })); // Only re-renders when todos change function TodoList() { const todos = useStore((state) => state.todos); return <div>{/* ... */}</div>; } // Never re-renders function AddTodo() { const addTodo = useStore((state) => state.addTodo); return <button onClick={() => addTodo({ text: "New" })}>Add</button>; }

TanStack Query for Server State

const { data } = useQuery({ queryKey: ["users"], queryFn: fetchUsers, staleTime: 5 * 60 * 1000, // 5 minutes });

Automatic caching, deduplication, background refetching.

Bundle Size Optimization

Tree Shaking

// Bad: Imports entire library import _ from "lodash"; // Good: Import only what you need import debounce from "lodash/debounce";

Analyze Bundle

npm install --save-dev vite-plugin-bundle-analyzer

Find and remove large dependencies.

React Compiler (Coming Soon)

React 19+ will have automatic memoization. But until then, manual optimization is needed.

Performance Checklist

✅ Profile before optimizing ✅ Use React.memo for expensive components ✅ Virtualize large lists ✅ Code split routes ✅ Lazy load images ✅ Optimize images (WebP, sizing) ✅ Memoize expensive computations ✅ Use proper state management ✅ Analyze bundle size ✅ Monitor Core Web Vitals

Conclusion

Performance is a feature. Build fast from the start:

  • Measure with tools
  • Optimize bottlenecks
  • Use proper patterns
  • Monitor in production

Fast apps = happy users = business success.


Need help with React performance? Let's talk.

Found this helpful?

Let's discuss your project needs.

Get in touch