| name | react-performance |
| user-invocable | false |
| description | Use when React performance optimization including memoization, lazy loading, and virtualization. Use when optimizing React applications. |
| allowed-tools | ["Bash","Read"] |
React Performance Optimization
Master react performance optimization for building high-performance, scalable
React applications with industry best practices.
React.memo and Component Memoization
React.memo prevents unnecessary re-renders by memoizing component output:
import { memo } from 'react';
interface Props {
name: string;
onClick: () => void;
}
const ExpensiveComponent = memo(
function ExpensiveComponent({ name, onClick }: Props) {
console.log('Rendering ExpensiveComponent');
return <button onClick={onClick}>{name}</button>;
});
const CustomMemo = memo(
function Component({ user }: { user: User }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);
const ProductCard = memo(
function ProductCard({ product }: { product: Product }) {
return (
<div>
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
);
},
(prev, next) => {
return (
prev.product.id === next.product.id &&
prev.product.name === next.product.name &&
prev.product.price === next.product.price
);
}
);
useMemo for Expensive Computations
import { useMemo, useState } from 'react';
function DataTable({ items }: { items: Item[] }) {
const [filter, setFilter] = useState('');
const [sortBy, setSortBy] = useState<'name' | 'price'>('name');
const processedItems = useMemo(() => {
console.log('Computing filtered and sorted items');
return items
.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()))
.sort((a, b) => {
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
}
return a.price - b.price;
});
}, [items, filter, sortBy]);
const statistics = useMemo(() => {
console.log('Computing statistics');
return {
: processedItems.( sum + item., ),
: processedItems.
? processedItems.( sum + item., ) / processedItems.
: ,
: processedItems.
};
}, [processedItems]);
(
);
}
useCallback for Stable Function References
import { useCallback, useState, memo } from 'react';
const ListItem = memo(function ListItem({
item,
onDelete
}: {
item: Item;
onDelete: (id: string) => void;
}) {
console.log('Rendering ListItem', item.id);
return (
<div>
{item.name}
<button onClick={() => onDelete(item.id)}>Delete</button>
</div>
);
});
function OptimizedList({ items }: { items: Item[] }) {
const [deletedIds, setDeletedIds] = useState<Set<string>>(new Set());
const handleDelete = useCallback((id: string) => {
setDeletedIds(prev => new Set([...prev, id]));
api.(id);
}, []);
handleDeleteWithDeps = ( {
.(, deletedIds.);
( ([...prev, id]));
}, [deletedIds]);
visibleItems = items.( !deletedIds.(item.));
(
);
}
Code Splitting with React.lazy and Suspense
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));
function LoadingSpinner() {
return <div className="spinner">Loading...</div>;
}
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
} />
} />
} />
} />
);
}
() {
= () => ();
= () => ();
(
);
}
() {
= ( ());
= ( ());
= ( ());
(
);
}
Virtual Scrolling for Large Lists
import { FixedSizeList, VariableSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer';
function VirtualList({ items }: { items: string[] }) {
const Row = ({ index, style }: {
index: number;
style: React.CSSProperties
}) => (
<div style={style} className="list-item">
{items[index]}
</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={35}
width="100%"
>
{Row}
</FixedSizeList>
);
}
function VariableList({ items }: { items: Post[] }) {
const getItemSize = (index: number) => {
return items[index].content. > ? : ;
};
= () => (
);
(
);
}
() {
(
);
}
React Profiler API for Performance Monitoring
import { Profiler, ProfilerOnRenderCallback } from 'react';
const onRenderCallback: ProfilerOnRenderCallback = (
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
interactions
) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
if (actualDuration > 100) {
analytics.track('slow-render', {
component: id,
duration: actualDuration,
phase
});
}
};
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<Dashboard />
</Profiler>
);
}
function () {
(
);
}
Bundle Size Optimization
function ChartComponent() {
const [Chart, setChart] = useState<any>(null);
useEffect(() => {
import('chart.js').then(module => {
setChart(() => module.Chart);
});
}, []);
if (!Chart) return <div>Loading chart...</div>;
return <Chart data={data} />;
}
import { format } from 'date-fns';
import moment from 'moment';
const AdminPanel = lazy(() =>
import( './AdminPanel')
);
const = (
( )
);
Image Optimization Techniques
import { useState, useEffect } from 'react';
function LazyImage({ src, alt, placeholder }: {
src: string;
alt: string;
placeholder?: string;
}) {
const [imageSrc, setImageSrc] = useState(placeholder || '');
const [imageRef, setImageRef] = useState<HTMLImageElement | null>(null);
useEffect(() => {
if (!imageRef) return;
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.unobserve(imageRef);
}
});
});
observer.observe(imageRef);
return () => {
if (imageRef) observer.unobserve(imageRef);
};
}, [imageRef, src]);
return (
<img
ref={setImageRef}
src={imageSrc}
alt={alt}
loading=
/>
);
}
() {
[currentSrc, setCurrentSrc] = (placeholder);
[loading, setLoading] = ();
( {
img = ();
img. = src;
img. = {
(src);
();
};
}, [src]);
(
);
}
Concurrent Features: useTransition and useDeferredValue
import { useState, useTransition, useDeferredValue } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<Result[]>([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (value: string) => {
setQuery(value);
startTransition(() => {
const searchResults = performExpensiveSearch(value);
setResults(searchResults);
});
};
return (
<>
<input
value={query}
onChange={e => handleSearch(e.target.value)}
placeholder="Search..."
/>
{isPending && <div>Searching...</div>}
<ResultsList results={results} />
</>
);
}
function ProductList() {
[query, setQuery] = ();
deferredQuery = (query);
filteredProducts = ( {
products.(
p..().(deferredQuery.())
);
}, [products, deferredQuery]);
(
);
}
Debouncing and Throttling
import { useState, useEffect, useCallback, useRef } from 'react';
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
function SearchWithDebounce() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500);
useEffect(() => {
if (debouncedQuery) {
performSearch(debouncedQuery);
}
}, [debouncedQuery]);
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}
function useThrottle<T>(value: T, limit: number): T {
[throttledValue, setThrottledValue] = useState<T>(value);
lastRan = (.());
( {
handler = ( {
(.() - lastRan. >= limit) {
(value);
lastRan. = .();
}
}, limit - (.() - lastRan.));
(handler);
}, [value, limit]);
throttledValue;
}
() {
[scrollPosition, setScrollPosition] = ();
throttledScroll = (scrollPosition, );
( {
= () => (.);
.(, handleScroll);
.(, handleScroll);
}, []);
( {
.(, throttledScroll);
}, [throttledScroll]);
;
}
Optimizing Context Performance
import { createContext, useContext, useState, useMemo, ReactNode } from 'react';
const StateContext = createContext<State | null>(null);
const DispatchContext = createContext<Dispatch | null>(null);
function Provider({ children }: { children: ReactNode }) {
const [state, setState] = useState<State>(initialState);
const dispatch = useMemo(
() => ({
updateUser: (user: User) => setState(s => ({ ...s, user })),
updateSettings: (settings: Settings) => setState(s => ({ ...s, settings }))
}),
[]
);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</>
);
}
() {
state = ();
;
}
() {
dispatch = ();
;
}
Web Workers for Heavy Computations
import { useEffect, useState } from 'react';
function useWebWorker<T, R>(workerFn: (data: T) => R) {
const [result, setResult] = useState<R | null>(null);
const [error, setError] = useState<Error | null>(null);
const [loading, setLoading] = useState(false);
const execute = (data: T) => {
setLoading(true);
setError(null);
const worker = new Worker(
URL.createObjectURL(
new Blob([`(${workerFn.toString()})()`], { type: 'application/javascript' })
)
);
worker.postMessage(data);
worker.onmessage = (e) => {
setResult(e.data);
setLoading();
worker.();
};
worker. = {
( (e.));
();
worker.();
};
};
{ result, error, loading, execute };
}
() {
{ result, loading, execute } = (
{
data.( sum + n * n, );
}
);
= () => {
(.({ : }, i));
};
(
);
}
When to Use This Skill
Use react-performance when you need to:
- Optimize slow-rendering components
- Reduce bundle size with code splitting
- Handle large lists with virtualization
- Prevent unnecessary re-renders
- Improve application load time
- Optimize expensive computations
- Build performant React applications
- Debug performance issues
- Implement lazy loading strategies
- Improve Core Web Vitals scores
- Optimize for mobile devices
- Handle real-time data efficiently
Best Practices
-
Profile before optimizing - Use React DevTools Profiler to identify actual
bottlenecks before applying optimizations.
-
Use React.memo wisely - Only memoize components that render often with the
same props or have expensive render logic.
-
Memoize callbacks and values - Use useCallback for functions passed to
memoized children, useMemo for expensive computations.
-
Code split by route - Lazy load route components to reduce initial bundle
size and improve load time.
-
Virtualize long lists - Use react-window or react-virtualized for lists
with more than 100 items.
-
Optimize images - Lazy load images, use appropriate formats (WebP),
implement progressive loading.
-
Debounce expensive operations - Debounce search inputs, API calls, and
other expensive operations.
-
Split context strategically - Separate read and write contexts to prevent
unnecessary consumer re-renders.
-
Monitor bundle size - Use webpack-bundle-analyzer to identify and remove
large dependencies.
-
Use concurrent features - Leverage useTransition and useDeferredValue for
better perceived performance.
Common Pitfalls
-
Premature optimization - Don't optimize without measuring. Profile first,
then optimize bottlenecks.
-
Overusing memo - Memoizing everything adds overhead. Only memoize when
there's a measurable benefit.
-
Wrong dependencies - Missing dependencies in useMemo/useCallback leads to
stale closures and bugs.
-
Not measuring impact - Always measure performance improvements with React
Profiler or browser tools.
-
Ignoring bundle size - Importing large libraries for small features
significantly impacts load time.
-
Memoizing primitives - useMemo is unnecessary for primitive values or
simple calculations.
-
Not using key prop - Missing or incorrect keys in lists cause unnecessary
re-renders and bugs.
-
Inline function definitions - Creating functions inline in JSX prevents
React.memo from working effectively.
-
Not code splitting - Loading entire app upfront increases initial load
time dramatically.
-
Forgetting about network - Optimize data fetching, use pagination,
implement proper caching strategies.
Resources