Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Standardized guidelines and patterns for Frontend React Performance Optimization.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 57% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 36% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 59% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 130% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 51% | 0% |
Prevent unnecessary re-renders of components:
typescript// Without memo - re-renders on every parent update export const ExpensiveComponent = ({ data }: { data: string }) => { console.log('Rendering ExpensiveComponent'); return <div>{data}</div>; }; // With memo - only re-renders when data changes export const ExpensiveComponent = React.memo(({ data }: { data: string }) => { console.log('Rendering ExpensiveComponent'); return <div>{data}</div>; }); // Custom comparison function export const UserCard = React.memo( ({ user }: { user: User }) => <div>{user.name}</div>, (prevProps, nextProps) => { // Only re-render if user.id changed return prevProps.user.id === nextProps.user.id; } );
Memoize expensive calculations:
typescriptfunction ProductList({ products, searchTerm }: Props) { // ❌ Bad - filters on every render const filtered = products.filter(p => p.name.includes(searchTerm) ); // ✅ Good - only recalculates when dependencies change const filteredProducts = useMemo(() => { console.log('Filtering products'); return products.filter(p => p.name.includes(searchTerm)); }, [products, searchTerm]); return <div>{filteredProducts.map(...)}</div>; }
Memoize function references:
typescriptfunction Parent() { const [count, setCount] = useState(0); const [other, setOther] = useState(0); // ❌ Bad - new function on every render const handleClick = () => { setCount(count + 1); }; // ✅ Good - same function reference const handleClick = useCallback(() => { setCount(c => c + 1); }, []); // Empty deps because we use functional update return <ExpensiveChild onClick={handleClick} />; } const ExpensiveChild = React.memo(({ onClick }: { onClick: () => void }) => { console.log('Child rendered'); return <button onClick={onClick}>Click</button>; });
Route-based splitting:
typescriptimport { lazy, Suspense } from 'react'; const Dashboard = lazy(() => import('./pages/Dashboard')); const Settings = lazy(() => import('./pages/Settings')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/settings" element={<Settings />} /> </Routes> </Suspense> ); }
Component-based splitting:
typescriptconst HeavyChart = lazy(() => import('./components/HeavyChart')); function Analytics() { const [showChart, setShowChart] = useState(false); return ( <div> <button onClick={() => setShowChart(true)}>Show Chart</button> {showChart && ( <Suspense fallback={<Skeleton />}> <HeavyChart data={data} /> </Suspense> )} </div> ); }
For long lists, render only visible items:
typescriptimport { FixedSizeList } from 'react-window'; function VirtualizedList({ items }: { items: string[] }) { const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => ( <div style={style}> {items[index]} </div> ); return ( <FixedSizeList height={600} itemCount={items.length} itemSize={50} width="100%" > {Row} </FixedSizeList> ); }
typescript// Debounce: Wait for pause in events function SearchInput() { const [search, setSearch] = useState(''); const debouncedSearch = useMemo( () => debounce((value: string) => { // API call fetchResults(value); }, 500), [] ); useEffect(() => { debouncedSearch(search); }, [search, debouncedSearch]); return <input value={search} onChange={e => setSearch(e.target.value)} />; } // Throttle: Limit frequency function ScrollHandler() { const handleScroll = useCallback( throttle(() => { console.log('Scroll position:', window.scrollY); }, 200), [] ); useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [handleScroll]); return <div>Scroll me</div>; }
typescript// ❌ Bad - runs expensive function on every render const [data, setData] = useState(expensiveComputation()); // ✅ Good - only runs once const [data, setData] = useState(() => expensiveComputation()); // Example const [user, setUser] = useState(() => { const stored = localStorage.getItem('user'); return stored ? JSON.parse(stored) : null; });
typescript// ❌ Bad - using index as key {items.map((item, index) => ( <Item key={index} data={item} /> ))} // ✅ Good - using stable unique identifier {items.map(item => ( <Item key={item.id} data={item} /> ))}
Mark non-urgent updates:
typescriptfunction SearchResults() { const [isPending, startTransition] = useTransition(); const [search, setSearch] = useState(''); const [results, setResults] = useState([]); const handleSearch = (value: string) => { setSearch(value); // Urgent: update input immediately startTransition(() => { // Non-urgent: filter can be delayed setResults(filterResults(value)); }); }; return ( <> <input value={search} onChange={e => handleSearch(e.target.value)} /> {isPending ? <Spinner /> : <ResultsList results={results} />} </> ); }
memo for expensive componentsuseMemo for expensive calculationsuseCallback for stable referencesOther measured skills in the registry, with their headline benchmark lift.