Optimize component rendering with React.memo, lazy loading, and code splitting strategies.
const UserCard = React.memo(({ user }) => (
<div>{user.name}</div>
));// Parent re-renders but UserCard doesn't if user prop is the same
const Parent = () => {
const [count, setCount] = useState(0);
const user = useMemo(() => ({ id: 1, name: "Alice" }), []);
return (
<>
<UserCard user={user} />
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
</>
);
};const UserCard = React.memo(
({ user }) => <div>{user.name}</div>,
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
}
);Use React.lazy to split code and load components on demand.
import { lazy, Suspense } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
export default function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<HeavyComponent />
</Suspense>
);
}
The component code is not included in the main bundle — it fetches only when first rendered.
Wrap lazy components in an ErrorBoundary to handle chunk load failures gracefully.
<ErrorBoundary fallback={<p>Failed to load — check your connection.</p>}>
<Suspense fallback={<p>Loading...</p>}>
<HeavyComponent />
</Suspense>
</ErrorBoundary>
Load conditional UI only when needed — modals, settings panels, and charts are ideal candidates.
const SettingsPanel = lazy(() => import("./SettingsPanel"));
{showSettings && (
<Suspense fallback={<p>Loading settings...</p>}>
<SettingsPanel />
</Suspense>
)}
Works great for route-based code splitting in React Router or any SPA router.
import { lazy } from "react";
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
const Profile = lazy(() => import("./pages/Profile"));<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</Routes>const Header = React.memo(() => <header>...</header>);
const Content = React.memo(({ data }) => <main>{data}</main>);
const Footer = React.memo(() => <footer>...</footer>);
export default function Page({ data }) {
return (
<>
<Header />
<Content data={data} />
<Footer />
</>
);
}const handleClick = useCallback(() => {
// Handle click
}, []);
<Button onClick={handleClick} />import { Profiler } from "react";
<Profiler id="dashboard" onRender={onRender}>
<Dashboard />
</Profiler>
function onRender(id, phase, actualDuration) {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
}// Profile to find slow components first
// Then apply memo, lazy, or splitting strategicallyUse React.memo to prevent a component from re-rendering when its props haven't changed. Use useMemo inside a component to memoize expensive computed values. They solve different problems and are often used together.
Wrap your import with React.lazy(() => import('./MyComponent')) and render it inside a Suspense boundary with a fallback UI. The component's code is only fetched when it's first rendered.
No — children is a new object reference on every render, so React.memo will always re-render if you pass JSX as children. Lift the children out or restructure your component tree to avoid this.
Route-based splitting lazy-loads entire page-level components when a route is first visited, which is the highest-impact change for initial load time. Component splitting targets large UI pieces (modals, charts) that aren't visible on first paint.
Use the Coverage tab in Chrome DevTools to see how much JavaScript is unused on initial load, and check the Network tab to confirm chunks are loaded on demand. React DevTools Profiler shows which components re-render unnecessarily.