| name | performance-optimization |
| description | Optimize React application performance with lazy loading, code splitting, memoization, and virtualization. Use when addressing slow renders, large bundles, or memory issues. |
Performance Optimization
When to Use This Skill
Use when:
- Initial load time is too slow
- Components re-render unnecessarily
- Rendering large lists or data sets
- Bundle size is too large
- Memory usage is high
Code Splitting & Lazy Loading
React.lazy for Route-based Splitting
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
Named Exports with Lazy
const LazyComponent = lazy(() =>
import('./components').then(module => ({
default: module.SpecificComponent
}))
);
Preloading on Hover
const DashboardLazy = lazy(() => import('./Dashboard'));
function NavLink() {
const preload = () => {
import('./Dashboard');
};
return (
<Link to="/dashboard" onMouseEnter={preload}>
Dashboard
</Link>
);
}
Memoization
React.memo for Components
interface ExpensiveComponentProps {
data: Data;
onAction: (id: string) => void;
}
export const ExpensiveComponent = React.memo(
function ExpensiveComponent({ data, onAction }: ExpensiveComponentProps) {
return <div>{/* expensive render */}</div>;
}
);
export const CustomMemoComponent = React.memo(
ExpensiveComponent,
(prevProps, nextProps) => {
return prevProps.data.id === nextProps.data.id;
}
);
useMemo for Expensive Calculations
function DataProcessor({ items, filter }: Props) {
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.includes(filter)
).sort((a, b) => a.name.localeCompare(b.name));
}, [items, filter]);
return <List items={filteredItems} />;
}
useCallback for Stable References
function Parent({ items }: Props) {
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []);
return (
<>
{items.map(item => (
<MemoizedChild
key={item.id}
onClick={handleClick}
/>
))}
</>
);
}
List Virtualization
Using react-window
import { FixedSizeList } from 'react-window';
interface ItemData {
items: Item[];
onSelect: (id: string) => void;
}
function VirtualList({ items, onSelect }: ItemData) {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style} onClick={() => onSelect(items[index].id)}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={400}
width="100%"
itemCount={items.length}
itemSize={50}
>
{Row}
</FixedSizeList>
);
}
Debouncing & Throttling
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
function SearchInput() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) {
search(debouncedQuery);
}
}, [debouncedQuery]);
}
Bundle Analysis
npx vite-bundle-visualizer
npx webpack-bundle-analyzer stats.json
Performance Checklist