React Performance: 8 Fixes That Actually Matter (2026)

React Performance: 8 Fixes That Actually Matter (2026)

React Performance: 8 Fixes That Actually Matter (2026) I audited 15 React apps. These 8 optimizations appeared in every slow one. 1. Stop Re-rendering Everything // ❌ BAD: Every keystroke re-renders the entire form function Form() { const [name, setName] = useState(''); const [email, setEmail] = useState(''); return ( {/* Re-renders on every keystroke! */} setName(e.target.value)} /> setEmail(e.target.value)} /> ); } // ✅ GOOD: Memoize expensive components function Form() { const [name, setName] = useState(''); const [email, setEmail] = useState(''); return ( setName(e.target.value)} /> setEmail(e.target.value)} /> ); } const MemoizedChart = React.memo(function Chart({ data }) { return ; }); Enter fullscreen mode Exit fullscreen mode 2. Use the Right State Management // ❌ BAD: Context re-renders ALL consumers on ANY change const ThemeContext = createContext(); const UserContext = createContext(); function App() { const [theme, setTheme] = useState('dark'); const [user, setUser] = useState(null); return ( {/* Re-renders when theme OR user changes */} ); } // ✅ GOOD: Split contexts + use libraries for complex state // Option 1: Separate contexts (simple apps) // Option 2: Use Zustand (complex apps) import { create } from 'zustand'; const useTheme = create((set) => ({ theme: 'dark', setTheme: (t) => set({ theme: t }), })); // Components only re-render when THEIR slice changes function ThemeToggle() { const theme = useTheme(s => s.theme); // Only re-renders if theme changes const setTheme = useTheme(s => s.setTheme); return setTheme(theme === 'dark' ? 'light' : 'dark')}>Toggle; } Enter fullscreen mode Exit fullscreen mode 3. Lazy Load Routes // ❌ BAD: All page components loaded upfront import Home from './pages/Home'; import Dashboard from './pages/Dashboard'; import Settings from './pages/Settings'; // Bundle includes ALL pages even if user never visits them // ✅ GOOD: Lazy load each route import { lazy, Suspense } from 'react'; const Home = lazy(() => import('./pages/Home')); const Dashboard = lazy(() => import('./pages/Dashboard')); const Settings = lazy(() => import('./pages/Settings')); function App() { return ( }> } /> } /> } /> ); } Enter fullscreen mode Exit fullscreen mode Impact: Dashboard app bundle went from 1.2MB to 180KB initial load. 4. Virtualize Long Lists // ❌ BAD: Renders ALL 10,000 items function UserList({ users }) { return ( {users.map(user => ( ))} ); } // ✅ GOOD: Only renders visible items import { useVirtualizer } from '@tanstack/react-virtual'; function UserList({ users }) { const parentRef = useRef(null); const virtualizer = useVirtualizer({ count: users.length, getScrollElement: () => parentRef.current, estimateSize: () => 48, // Estimated row height }); return ( {virtualizer.getVirtualItems().map(virtualItem => ( ))} ); } Enter fullscreen mode Exit fullscreen mode Impact: 10,000 row table: render time dropped from 4.2s to 16ms. 5. Debounce Expensive Operations // ❌ BAD: Search fires on every keystroke function SearchBar() { const [query, setQuery] = useState(''); const handleChange = (e) => { setQuery(e.target.value); searchAPI(e.target.value); // API call on EVERY keystroke! }; return ; } // ✅ GOOD: Debounce the API call import { useDebouncedValue } from '@mantine/hooks'; function SearchBar() { const [query, setQuery] = useState(''); const [debouncedQuery] = useDebouncedValue(query, 300); useEffect(() => { if (debouncedQuery) { searchAPI(debouncedQuery); } }, [debouncedQuery]); return setQuery(e.target.value)} />; } Enter fullscreen mode Exit fullscreen mode 6. Optimize Images // ❌ BAD: Full resolution images // ✅ GOOD: Responsive images + lazy loading + WebP Enter fullscreen mode Exit fullscreen mode Impact: Page weight dropped from 8MB to 1.2MB after converting to WebP + lazy loading. 7. Use useCallback and useMemo Wisely // ✅ DO: Memoize callbacks passed to children function Parent() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('clicked'); }, []); // Stable reference — child won't re-render return ; } // ❌ DON'T: Memoize everything "just in case" function BadComponent() { const value = useMemo(() => 2 + 2, []); // Overkill — this is trivial const fn = useCallback(() => 'hello', []); // Overkill — no child depends on it return {value}; } // Rule: Only memoize if the value is expensive to compute // OR if it's passed to a memoized child component Enter fullscreen mode Exit fullscreen mode 8. Measure Before Optimizing // React DevTools Profiler — built-in! // 1. Open React DevTools → Profiler tab // 2. Click record // 3. Interact with your app // 4. Stop recording // 5. See exactly which components re-rendered and why // Chrome Performance tab for non-React performance: // 1. F12 → Performance tab // 2. Record // 3. Interact // 4. Stop // 5. Check for long tasks (>50ms), layout thrashing, paint // Web Vitals in code: import { onCLS, onFID, onLCP, onINP } from 'web-vitals'; onCLS(console.log); // Cumulative Layout Shift (< 0.1) onFID(console.log); // First Input Delay (< 100ms) onLCP(console.log); // Largest Contentful Paint (< 2.5s) onINP(console.log); // Interaction to Next Paint (< 200ms) Enter fullscreen mode Exit fullscreen mode The Quick Wins Checklist Fix Effort Impact Do It First? Lazy load routes Low High ✅ Image optimization Low High ✅ Debounce inputs Low Medium ✅ Virtualize lists Medium High For long lists Split contexts Medium High For complex state React.memo Low Medium Only where needed useCallback/useMemo Low Low Only when needed State library Medium High For complex apps The golden rule: Measure first, optimize second. Don't guess. What's the biggest React performance fix you've made? Share your story! Follow @armorbreak for more React tips.

Original Source

Read the full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.