Skip to content
Vladimir Chavkov
Go back

React Performance Optimization: Complete Guide for 2025

Edit page

React Performance Optimization: Complete Guide for 2025

React performance optimization is crucial for building fast, responsive web applications. This comprehensive guide covers modern optimization techniques, from basic memoization to advanced rendering strategies, ensuring your React applications deliver optimal user experiences.

Performance Fundamentals

Understanding React Rendering

┌─────────────────────────────────────┐
│ Component Tree │
├─────────────────────────────────────┤
│ App │
│ ├── Header │
│ ├── Main │
│ │ ├── Sidebar │
│ │ └── Content │
│ │ ├── ItemList │
│ │ │ └── Item (x100) │
│ │ └── ItemDetail │
│ └── Footer │
└─────────────────────────────────────┘
Render Cascade: State Change → Virtual DOM → Reconciliation → DOM Updates

Performance Metrics

Key Performance Indicators

// Performance monitoring utility
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
}
startMeasure(name) {
this.metrics.set(name, performance.now());
}
endMeasure(name) {
const start = this.metrics.get(name);
if (start) {
const duration = performance.now() - start;
console.log(`${name}: ${duration.toFixed(2)}ms`);
this.metrics.delete(name);
return duration;
}
}
measureComponent(name, fn) {
this.startMeasure(name);
const result = fn();
this.endMeasure(name);
return result;
}
}
// Usage
const monitor = new PerformanceMonitor();
monitor.startMeasure('component-render');
// Component rendering logic
monitor.endMeasure('component-render');

Memoization Techniques

1. React.memo for Component Memoization

Basic Component Memoization

import React from 'react';
// Non-memoized component (re-renders on every parent render)
const ExpensiveComponent = ({ data, onClick }) => {
console.log('ExpensiveComponent rendered');
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: expensiveCalculation(item)
}));
}, [data]);
return (
<div>
{processedData.map(item => (
<div key={item.id} onClick={() => onClick(item.id)}>
{item.processed}
</div>
))}
</div>
);
};
// Memoized component (only re-renders when props change)
const MemoizedExpensiveComponent = React.memo(({ data, onClick }) => {
console.log('MemoizedExpensiveComponent rendered');
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: expensiveCalculation(item)
}));
}, [data]);
return (
<div>
{processedData.map(item => (
<div key={item.id} onClick={() => onClick(item.id)}>
{item.processed}
</div>
))}
</div>
);
});
// Custom comparison function for fine-grained control
const CustomMemoizedComponent = React.memo(
({ data, onClick }) => {
console.log('CustomMemoizedComponent rendered');
// Component logic
return <div>{/* JSX */}</div>;
},
(prevProps, nextProps) => {
// Only re-render if data length changed or onClick changed
return (
prevProps.data.length === nextProps.data.length &&
prevProps.onClick === nextProps.onClick
);
}
);

Advanced Memoization Strategies

// Memoize list components
const MemoizedListItem = React.memo(({ item, onSelect, isSelected }) => {
return (
<div
className={`item ${isSelected ? 'selected' : ''}`}
onClick={() => onSelect(item.id)}
>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
);
}, (prevProps, nextProps) => {
return (
prevProps.item.id === nextProps.item.id &&
prevProps.isSelected === nextProps.isSelected &&
prevProps.onSelect === nextProps.onSelect
);
});
// Memoize complex components with multiple props
const MemoizedCard = React.memo(({
user,
posts,
onUserClick,
onPostClick,
theme
}) => {
const userPosts = useMemo(() =>
posts.filter(post => post.userId === user.id),
[posts, user.id]
);
return (
<div className={`card theme-${theme}`}>
<UserProfile user={user} onClick={onUserClick} />
<PostList posts={userPosts} onPostClick={onPostClick} />
</div>
);
}, (prevProps, nextProps) => {
// Deep comparison for complex objects
return (
prevProps.user.id === nextProps.user.id &&
prevProps.posts.length === nextProps.posts.length &&
prevProps.theme === nextProps.theme &&
prevProps.onUserClick === nextProps.onUserClick &&
prevProps.onPostClick === nextProps.onPostClick
);
});

2. useMemo for Expensive Calculations

Calculation Memoization

import React, { useMemo } from 'react';
const DataProcessor = ({ rawData, filters, sortBy }) => {
// Memoize expensive data processing
const processedData = useMemo(() => {
console.log('Processing data...');
let filtered = rawData;
// Apply filters
if (filters.category) {
filtered = filtered.filter(item => item.category === filters.category);
}
if (filters.priceRange) {
filtered = filtered.filter(item =>
item.price >= filters.priceRange.min &&
item.price <= filters.priceRange.max
);
}
// Sort data
const sorted = [...filtered].sort((a, b) => {
switch (sortBy) {
case 'price':
return a.price - b.price;
case 'name':
return a.name.localeCompare(b.name);
case 'rating':
return b.rating - a.rating;
default:
return 0;
}
});
return sorted;
}, [rawData, filters, sortBy]);
// Memoize derived statistics
const statistics = useMemo(() => {
const total = processedData.length;
const averagePrice = processedData.reduce((sum, item) => sum + item.price, 0) / total || 0;
const averageRating = processedData.reduce((sum, item) => sum + item.rating, 0) / total || 0;
return {
total,
averagePrice,
averageRating,
priceRange: {
min: Math.min(...processedData.map(item => item.price)),
max: Math.max(...processedData.map(item => item.price))
}
};
}, [processedData]);
return (
<div>
<div className="statistics">
<p>Total items: {statistics.total}</p>
<p>Average price: ${statistics.averagePrice.toFixed(2)}</p>
<p>Average rating: {statistics.averageRating.toFixed(1)}</p>
</div>
<div className="data-list">
{processedData.map(item => (
<DataItem key={item.id} item={item} />
))}
</div>
</div>
);
};

Complex State Memoization

const ComplexStateComponent = ({ items, selectedItems, searchTerm }) => {
// Memoize filtered items
const filteredItems = useMemo(() => {
if (!searchTerm) return items;
return items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
item.description.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [items, searchTerm]);
// Memoize selected items map for quick lookup
const selectedItemsMap = useMemo(() => {
return new Map(selectedItems.map(item => [item.id, item]));
}, [selectedItems]);
// Memoize computed properties
const computedProperties = useMemo(() => {
const selectedCount = selectedItems.length;
const totalCount = filteredItems.length;
const isAllSelected = selectedCount === totalCount && totalCount > 0;
const isPartiallySelected = selectedCount > 0 && selectedCount < totalCount;
return {
selectedCount,
totalCount,
isAllSelected,
isPartiallySelected,
percentageSelected: totalCount > 0 ? (selectedCount / totalCount) * 100 : 0
};
}, [selectedItems.length, filteredItems.length]);
return (
<div>
<SelectionHeader {...computedProperties} />
<SearchBar searchTerm={searchTerm} />
<ItemList
items={filteredItems}
selectedItemsMap={selectedItemsMap}
/>
</div>
);
};

3. useCallback for Function Memoization

Event Handler Memoization

import React, { useState, useCallback } from 'react';
const TodoList = ({ todos, onUpdateTodo, onDeleteTodo }) => {
const [filter, setFilter] = useState('all');
// Memoize filter functions
const handleFilterChange = useCallback((newFilter) => {
setFilter(newFilter);
}, []);
// Memoize todo handlers
const handleToggleTodo = useCallback((todoId) => {
const todo = todos.find(t => t.id === todoId);
if (todo) {
onUpdateTodo(todoId, { completed: !todo.completed });
}
}, [todos, onUpdateTodo]);
const handleDeleteTodo = useCallback((todoId) => {
onDeleteTodo(todoId);
}, [onDeleteTodo]);
// Memoize filtered todos
const filteredTodos = useMemo(() => {
switch (filter) {
case 'completed':
return todos.filter(todo => todo.completed);
case 'active':
return todos.filter(todo => !todo.completed);
default:
return todos;
}
}, [todos, filter]);
return (
<div>
<FilterButtons
currentFilter={filter}
onFilterChange={handleFilterChange}
/>
<TodoListItems
todos={filteredTodos}
onToggle={handleToggleTodo}
onDelete={handleDeleteTodo}
/>
</div>
);
};
// Optimized TodoItem component
const TodoItem = React.memo(({ todo, onToggle, onDelete }) => {
const handleToggle = useCallback(() => {
onToggle(todo.id);
}, [todo.id, onToggle]);
const handleDelete = useCallback(() => {
onDelete(todo.id);
}, [todo.id, onDelete]);
return (
<div className={`todo ${todo.completed ? 'completed' : ''}`}>
<input
type="checkbox"
checked={todo.completed}
onChange={handleToggle}
/>
<span>{todo.text}</span>
<button onClick={handleDelete}>Delete</button>
</div>
);
});

API Call Memoization

const useApiData = (url, dependencies = []) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Memoize fetch function
const fetchData = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData, ...dependencies]);
return { data, loading, error, refetch: fetchData };
};
// Usage in component
const DataComponent = ({ userId, refreshTrigger }) => {
const { data, loading, error, refetch } = useApiData(
`/api/users/${userId}`,
[userId, refreshTrigger]
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<UserProfile user={data} />
<button onClick={refetch}>Refresh</button>
</div>
);
};

Code Splitting and Lazy Loading

1. Route-Based Code Splitting

React.lazy Implementation

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import LoadingSpinner from './components/LoadingSpinner';
// Lazy load components
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
const App = () => {
return (
<Router>
<nav>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
<Link to="/profile">Profile</Link>
<Link to="/settings">Settings</Link>
</nav>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</Router>
);
};
// Loading component with skeleton
const LoadingSpinner = () => (
<div className="loading-container">
<div className="skeleton">
<div className="skeleton-header"></div>
<div className="skeleton-content">
<div className="skeleton-line"></div>
<div className="skeleton-line"></div>
<div className="skeleton-line"></div>
</div>
</div>
</div>
);

Advanced Route Splitting

// Preload routes for better UX
const preloadRoute = (componentImport) => {
const Component = lazy(componentImport);
// Preload component on hover
Component.preload = componentImport;
return Component;
};
const Home = preloadRoute(() => import('./pages/Home'));
const Dashboard = preloadRoute(() => import('./pages/Dashboard'));
// Navigation component with preloading
const Navigation = () => {
const handleMouseEnter = (component) => {
if (component.preload) {
component.preload();
}
};
return (
<nav>
<Link
to="/"
onMouseEnter={() => handleMouseEnter(Home)}
>
Home
</Link>
<Link
to="/dashboard"
onMouseEnter={() => handleMouseEnter(Dashboard)}
>
Dashboard
</Link>
</nav>
);
};

2. Component-Based Code Splitting

Dynamic Component Loading

import React, { useState, Suspense } from 'react';
// Dynamic component loader
const DynamicComponent = ({ componentName, ...props }) => {
const [Component, setComponent] = useState(null);
React.useEffect(() => {
const loadComponent = async () => {
try {
const module = await import(`./components/${componentName}`);
setComponent(() => module.default);
} catch (error) {
console.error(`Failed to load component: ${componentName}`, error);
setComponent(() => () => <div>Error loading component</div>);
}
};
loadComponent();
}, [componentName]);
if (!Component) {
return <div>Loading component...</div>;
}
return (
<Suspense fallback={<div>Loading...</div>}>
<Component {...props} />
</Suspense>
);
};
// Usage
const App = () => {
const [activeComponent, setActiveComponent] = useState('UserProfile');
return (
<div>
<div className="controls">
<button onClick={() => setActiveComponent('UserProfile')}>
Profile
</button>
<button onClick={() => setActiveComponent('UserSettings')}>
Settings
</button>
</div>
<DynamicComponent
componentName={activeComponent}
userId="123"
/>
</div>
);
};

Conditional Component Loading

const ConditionalLoader = ({ feature, children }) => {
const [FeatureComponent, setFeatureComponent] = useState(null);
React.useEffect(() => {
if (feature) {
import(`./features/${feature}`)
.then(module => {
setFeatureComponent(() => module.default);
})
.catch(error => {
console.error(`Failed to load feature: ${feature}`, error);
});
}
}, [feature]);
if (!feature || !FeatureComponent) {
return children;
}
return (
<Suspense fallback={<div>Loading feature...</div>}>
<FeatureComponent />
</Suspense>
);
};
// Usage
const Dashboard = ({ userRole }) => {
return (
<div>
<h1>Dashboard</h1>
<ConditionalLoader feature="Analytics">
<div>Basic dashboard content</div>
</ConditionalLoader>
{userRole === 'admin' && (
<ConditionalLoader feature="AdminPanel">
<div>Admin features</div>
</ConditionalLoader>
)}
</div>
);
};

Virtualization and Windowing

1. react-window for Large Lists

Basic Virtual List

import { FixedSizeList as List } from 'react-window';
const VirtualizedList = ({ items }) => {
const Row = ({ index, style }) => (
<div style={style}>
<ListItem item={items[index]} />
</div>
);
return (
<List
height={600}
itemCount={items.length}
itemSize={80}
width="100%"
>
{Row}
</List>
);
};
// Memoized list item
const ListItem = React.memo(({ item }) => {
return (
<div className="list-item">
<h3>{item.title}</h3>
<p>{item.description}</p>
<span className="price">${item.price}</span>
</div>
);
});

Advanced Virtualization with Variable Heights

import { VariableSizeList as List } from 'react-window';
const VariableHeightList = ({ items }) => {
const getItemSize = (index) => {
// Calculate item height based on content
const item = items[index];
const baseHeight = 80;
const extraLines = Math.floor(item.description.length / 50);
return baseHeight + (extraLines * 20);
};
const Row = ({ index, style }) => (
<div style={style}>
<VariableHeightItem item={items[index]} />
</div>
);
return (
<List
height={600}
itemCount={items.length}
itemSize={getItemSize}
width="100%"
>
{Row}
</List>
);
};
// Optimized item component with measurements
const VariableHeightItem = React.memo(({ item }) => {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className="variable-item">
<div className="item-header">
<h3>{item.title}</h3>
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Collapse' : 'Expand'}
</button>
</div>
<p className="item-description">{item.description}</p>
{isExpanded && (
<div className="item-details">
<p>Created: {new Date(item.createdAt).toLocaleDateString()}</p>
<p>Category: {item.category}</p>
<p>Tags: {item.tags.join(', ')}</p>
</div>
)}
</div>
);
});

2. react-virtualized for Complex Grids

Virtualized Table

import { Table, Column } from 'react-virtualized';
const VirtualizedTable = ({ data }) => {
const rowGetter = ({ index }) => data[index];
return (
<Table
width={800}
height={400}
headerHeight={40}
rowHeight={50}
rowCount={data.length}
rowGetter={rowGetter}
>
<Column
label="ID"
dataKey="id"
width={100}
/>
<Column
label="Name"
dataKey="name"
width={200}
/>
<Column
label="Email"
dataKey="email"
width={250}
/>
<Column
label="Status"
dataKey="status"
width={150}
/>
<Column
label="Actions"
dataKey="actions"
width={100}
cellRenderer={({ cellData }) => (
<button onClick={cellData.onEdit}>Edit</button>
)}
/>
</Table>
);
};

State Management Optimization

1. Context API Optimization

Context Splitting

// Split contexts to prevent unnecessary re-renders
const UserContext = React.createContext();
const ThemeContext = React.createContext();
const SettingsContext = React.createContext();
// User provider
const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const value = useMemo(() => ({
user,
setUser,
loading,
setLoading
}), [user, loading]);
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
};
// Theme provider
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({
theme,
setTheme,
toggleTheme: () => setTheme(prev => prev === 'light' ? 'dark' : 'light')
}), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};
// Optimized consumer components
const UserProfile = () => {
const { user } = useContext(UserContext);
// Only re-renders when user changes
return <div>Welcome, {user?.name}</div>;
};
const ThemeToggle = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
// Only re-renders when theme changes
return (
<button onClick={toggleTheme}>
Current theme: {theme}
</button>
);
};

Selective Context Updates

// Optimized context with selectors
const createContextWithSelector = (defaultValue) => {
const Context = React.createContext(defaultValue);
const Provider = ({ value, children }) => {
const memoizedValue = useMemo(() => value, [value]);
return (
<Context.Provider value={memoizedValue}>
{children}
</Context.Provider>
);
};
const useContextSelector = (selector) => {
const value = useContext(Context);
return selector(value);
};
return [Provider, useContextSelector];
};
// Usage
const [AppStateProvider, useAppStateSelector] = createContextWithSelector({
user: null,
theme: 'light',
notifications: []
});
const App = () => {
const [state, setState] = useState({
user: null,
theme: 'light',
notifications: []
});
return (
<AppStateProvider value={state}>
<Dashboard />
</AppStateProvider>
);
};
// Components only re-render when their selected data changes
const UserName = () => {
const userName = useAppStateSelector(state => state.user?.name);
return <div>{userName}</div>;
};
const ThemeIndicator = () => {
const theme = useAppStateSelector(state => state.theme);
return <div>Theme: {theme}</div>;
};

2. Redux Optimization

Memoized Selectors

import { createSelector } from 'reselect';
// Base selectors
const selectItems = state => state.items;
const selectFilter = state => state.filter;
const selectSortBy = state => state.sortBy;
// Memoized derived selector
const selectFilteredItems = createSelector(
[selectItems, selectFilter],
(items, filter) => {
if (!filter) return items;
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}
);
// Memoized sorted selector
const selectSortedItems = createSelector(
[selectFilteredItems, selectSortBy],
(items, sortBy) => {
return [...items].sort((a, b) => {
switch (sortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'date':
return new Date(b.date) - new Date(a.date);
default:
return 0;
}
});
}
);
// Component usage
const ItemList = () => {
const items = useSelector(selectSortedItems);
return (
<div>
{items.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
};

Normalized State

import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
// Create entity adapter
const usersAdapter = createEntityAdapter({
selectId: user => user.id,
sortComparer: (a, b) => a.name.localeCompare(b.name)
});
// Create slice with normalized state
const usersSlice = createSlice({
name: 'users',
initialState: usersAdapter.getInitialState({
loading: false,
error: null
}),
reducers: {
addUser: usersAdapter.addOne,
addUsers: usersAdapter.addMany,
updateUser: usersAdapter.updateOne,
removeUser: usersAdapter.removeOne,
setLoading: (state, action) => {
state.loading = action.payload;
},
setError: (state, action) => {
state.error = action.payload;
}
}
});
// Export selectors
const usersSelectors = usersAdapter.getSelectors(
state => state.users
);
export const {
selectAll: selectAllUsers,
selectById: selectUserById,
selectIds: selectUserIds,
selectTotal: selectUserTotal
} = usersSelectors;

Rendering Optimization

1. Virtual DOM Optimization

Key Prop Optimization

// Bad: Using index as key
const BadList = ({ items }) => (
<ul>
{items.map((item, index) => (
<li key={index}>
{item.name}
</li>
))}
</ul>
);
// Good: Using stable unique keys
const GoodList = ({ items }) => (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
</li>
))}
</ul>
);
// Best: Composite keys for nested structures
const NestedList = ({ items }) => (
<ul>
{items.map(item => (
<li key={`${item.id}-${item.version}`}>
<h3>{item.name}</h3>
<ul>
{item.subItems.map(subItem => (
<li key={subItem.id}>
{subItem.name}
</li>
))}
</ul>
</li>
))}
</ul>
);

Conditional Rendering Optimization

// Bad: Creating new functions on every render
const BadComponent = ({ items, onItemClick }) => (
<div>
{items.map(item => (
<div
key={item.id}
onClick={() => onItemClick(item.id)}
>
{item.name}
</div>
))}
</div>
);
// Good: Memoized event handlers
const GoodComponent = React.memo(({ items, onItemClick }) => {
const handleClick = useCallback((itemId) => {
onItemClick(itemId);
}, [onItemClick]);
return (
<div>
{items.map(item => (
<ItemComponent
key={item.id}
item={item}
onClick={handleClick}
/>
))}
</div>
);
});
// Optimized item component
const ItemComponent = React.memo(({ item, onClick }) => {
const handleClick = useCallback(() => {
onClick(item.id);
}, [item.id, onClick]);
return (
<div onClick={handleClick}>
{item.name}
</div>
);
});

2. CSS-in-JS Optimization

Styled Components Optimization

import styled, { css } from 'styled-components';
// Memoized styled components
const StyledButton = styled.button`
${props => {
const base = css`
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
`;
switch (props.variant) {
case 'primary':
return css`
${base}
background-color: #007bff;
color: white;
&:hover {
background-color: #0056b3;
}
`;
case 'secondary':
return css`
${base}
background-color: #6c757d;
color: white;
&:hover {
background-color: #545b62;
}
`;
default:
return base;
}
}}
`;
// Optimized component with memoized styles
const OptimizedCard = React.memo(({ title, content, variant }) => {
return (
<StyledCard variant={variant}>
<h3>{title}</h3>
<p>{content}</p>
</StyledCard>
);
});
const StyledCard = styled.div`
${props => {
const base = css`
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;
switch (props.variant) {
case 'elevated':
return css`
${base}
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
`;
case 'outlined':
return css`
${base}
border: 1px solid #dee2e6;
box-shadow: none;
`;
default:
return base;
}
}}
`;

Performance Monitoring

1. React DevTools Profiler

Component Profiling

import { Profiler } from 'react';
const onRenderCallback = (id, phase, actualDuration) => {
console.log('Component render:', {
id,
phase,
actualDuration,
timestamp: performance.now()
});
};
const ProfiledComponent = ({ children }) => (
<Profiler id="App" onRender={onRenderCallback}>
{children}
</Profiler>
);
// Usage
const App = () => {
return (
<ProfiledComponent>
<ExpensiveComponent />
</ProfiledComponent>
);
};

Performance Metrics Hook

const usePerformanceMetrics = (componentName) => {
const renderCount = useRef(0);
const lastRenderTime = useRef(performance.now());
useEffect(() => {
renderCount.current += 1;
const currentTime = performance.now();
const timeSinceLastRender = currentTime - lastRenderTime.current;
console.log(`${componentName} render #${renderCount.current}: ${timeSinceLastRender.toFixed(2)}ms`);
lastRenderTime.current = currentTime;
});
return {
renderCount: renderCount.current,
lastRenderTime: lastRenderTime.current
};
};
// Usage
const MonitoredComponent = ({ data }) => {
const metrics = usePerformanceMetrics('MonitoredComponent');
return (
<div>
<div>Renders: {metrics.renderCount}</div>
{/* Component content */}
</div>
);
};

2. Web Vitals Integration

Performance Monitoring Hook

import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
const useWebVitals = () => {
useEffect(() => {
const handleMetric = (metric) => {
console.log('Web Vital:', metric);
// Send to analytics service
if (window.gtag) {
window.gtag('event', metric.name, {
value: Math.round(metric.value),
event_category: 'Web Vitals'
});
}
};
getCLS(handleMetric);
getFID(handleMetric);
getFCP(handleMetric);
getLCP(handleMetric);
getTTFB(handleMetric);
}, []);
};
// Usage in App component
const App = () => {
useWebVitals();
return (
<div>
{/* App content */}
</div>
);
};

Best Practices Summary

Component Optimization

Code Splitting

State Management

Rendering Optimization

Monitoring

Conclusion

React performance optimization requires a systematic approach combining proper memoization, code splitting, virtualization, and efficient state management. By implementing these techniques, you can build React applications that deliver excellent user experiences even with complex UIs and large datasets.

Key takeaways:

Remember that performance optimization is an ongoing process. Regular profiling and monitoring help identify bottlenecks and ensure your application continues to perform well as it grows in complexity.


Edit page
Share this post on:

Previous Post
VMware to Proxmox Migration: Complete Guide
Next Post
AWS Cost Optimization Strategies: Complete Guide