Shortcuts
BlogAugust 20, 2025

Mohamed Elbarry
Mastering React Performance Optimization
Performance optimization in React is both an art and a science. Understanding how React renders components and what causes unnecessary re-renders is essential.
Component Memoization
// Without memoization - re-renders on every parent update
function ExpensiveComponent({ data, onUpdate }) {
return (
  <div>
    {data.map(item => (
      <div key={item.id}>{item.name}</div>
    ))}
  </div>
);
}

// With memoization - only re-renders when props change
const MemoizedExpensiveComponent = React.memo(ExpensiveComponent);
useMemo Example
function ProductList({ products, filter, sortBy }) {
const filteredAndSortedProducts = useMemo(() => {
  return products
    .filter(product => 
      product.name.toLowerCase().includes(filter.toLowerCase())
    )
    .sort((a, b) => {
      if (sortBy === 'name') return a.name.localeCompare(b.name);
      if (sortBy === 'price') return a.price - b.price;
      return 0;
    });
}, [products, filter, sortBy]);

return (

<div>
{filteredAndSortedProducts.map((product) => (
  <ProductCard key={product.id} product={product} />
))}
</div>
); }
useCallback Example
function ParentComponent() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([]);

// With useCallback - function is memoized
const memoizedHandleAddItem = useCallback((item) => {
setItems(prev => [...prev, item]);
}, []);

return (

<div>
<Counter count={count} onIncrement={() => setCount((c) => c + 1)} />
<ItemList items={items} onAddItem={memoizedHandleAddItem} />
</div>
); }
Lazy Loading
import { Suspense, lazy } from 'react';

// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'));
const DataTable = lazy(() => import('./DataTable'));

function Dashboard() {
return (

<div>
<h1>Dashboard</h1>

    <Suspense fallback={<div>Loading chart...</div>}>
      <HeavyChart />
    </Suspense>

    <Suspense fallback={<div>Loading table...</div>}>
      <DataTable />
    </Suspense>
  </div>

);
}
Virtual Scrolling
function VirtualList({ items, itemHeight, containerHeight, renderItem }) {
const [scrollTop, setScrollTop] = useState(0);

const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + visibleCount + 1, items.length);

const visibleItems = items.slice(startIndex, endIndex);

return (

<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)} >
<div style={{ height: items.length * itemHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${startIndex * itemHeight}px)` }}>
{visibleItems.map((item, index) => (
<div key={startIndex + index} style={{ height: itemHeight }}>
{renderItem(item, startIndex + index)}
</div>
))}
</div>
</div>
</div>
);
}
Performance Monitoring
import { Profiler } from 'react';

function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
console.log('Component:', id);
console.log('Phase:', phase);
console.log('Actual duration:', actualDuration);
console.log('Base duration:', baseDuration);
}

function App() {
return (

<Profiler id='App' onRender={onRenderCallback}>
<YourComponent />
</Profiler>
); }
  • Measure First - Always profile before optimizing
  • Memoize Wisely - Don't overuse memoization
  • Split Code - Use code splitting for better initial load times
  • Optimize Images - Use Next.js Image component
  • Minimize Re-renders - Keep state close to where it's used
  • Use Keys Properly - Stable keys for list items
  • Avoid Inline Objects - Don't create objects in render methods
  • Lazy Load - Load components and routes on demand
The goal is to create applications that provide smooth, responsive user experiences while remaining maintainable.
Share this post: