🤯 Introduction
When I began using React, I was obsessed with making things work — not making them fast. Over time, I noticed lags, unnecessary re-renders, and slow UI. That’s when I dove deep into performance and found gold.
In this post, I’ll share React performance optimization tips I wish I knew earlier, so you can skip the slowdowns and jump straight to building snappy apps.
⚙️ 1. Use React.memo Smartly
React re-renders components even if the props haven’t changed — unless you wrap them in React.memo.
const Button = React.memo(({ onClick, children }) => {
console.log("Button rendered");
return <button onClick={onClick}>{children}</button>;
});
✅ Use it for pure components receiving primitive props.
❌ Don’t overuse — memo itself has an overhead.
🧠 2. Optimize with useCallback & useMemo
Prevent unnecessary re-renders or recalculations.
const handleClick = useCallback(() => {
console.log("Clicked!");
}, []);
const expensiveValue = useMemo(() => {
return computeHeavyStuff(data);
}, [data]);
These hooks keep references stable and prevent child components from rerendering.
💥 3. Avoid Inline Functions & Objects in JSX
Inline functions get recreated on every render:
// ❌ Triggers re-renders
<MyComponent onClick={() => doSomething()} />
// ✅ Better with useCallback
const handleClick = useCallback(() => doSomething(), []);
<MyComponent onClick={handleClick} />
Same applies to style={{}} and inline objects.
📦 4. Code-Splitting with React.lazy & Suspense
Don’t ship your entire app upfront.
const Settings = React.lazy(() => import('./Settings'));
<Suspense fallback={<Loader />}>
<Settings />
</Suspense>
📉 Keeps bundle size small
🚀 Faster initial page load
🧹 5. Clean Up useEffect Side Effects
Uncleared intervals or subscriptions lead to memory leaks.
useEffect(() => {
const id = setInterval(doSomething, 1000);
return () => clearInterval(id);
}, []);
Always clean up effects when using timers, listeners, or subscriptions.
🔄 6. Avoid Unnecessary Re-renders
Use tools like:
why-did-you-render- React DevTools Profiler
// Development only
import whyDidYouRender from '@welldone-software/why-did-you-render';
if (process.env.NODE_ENV === 'development') {
whyDidYouRender(React);
}
These help you see what’s rerendering and why.
🐢 7. Virtualize Long Lists
Rendering hundreds of DOM nodes? That’s slow.
Use react-window or react-virtualized:
import { FixedSizeList as List } from 'react-window';
<List height={400} itemCount={1000} itemSize={35} width={300}>
{({ index, style }) => <div style={style}>Item {index}</div>}
</List>
Only renders visible items. Huge performance gain.
🧯 8. Debounce Expensive Updates
Avoid re-rendering on every keystroke.
const debouncedSearch = useMemo(() => debounce(handleSearch, 300), []);
Use:
lodash.debounce-
use-debounce(React hook)
💡 9. Bonus Quick Wins
- Error Boundaries Prevent the entire UI from crashing:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
return this.state.hasError ? <Fallback /> : this.props.children;
}
}
- Skeleton Loaders Improve perceived performance:
<div className="h-6 w-full bg-gray-300 animate-pulse" />
- Intersection Observer with useRef Lazy load components only when in view.
const ref = useRef();
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
// Trigger load
}
});
if (ref.current) observer.observe(ref.current);
}, []);
🎯 Final Thoughts
React is powerful, but without understanding how rendering works, it’s easy to make your UI sluggish.
The key is to be intentional: Measure → Identify → Optimize.
I hope these tips help you build smoother, faster, and more efficient React apps!
Top comments (0)