React Memo & Code Splitting
Maximize rendering performance using React.memo, custom comparison heuristics, code splitting, and React Compiler optimizations.
TL;DR
- Skip redundant component re-renders using the
React.memowrapper. - Customize prop equality comparisons with custom
arePropsEqualfunctions. - Split application bundles on demand with dynamic
lazyimports.
Component Memoization with memo
Basic memo WrapperPrevent re-renders when scalar props remain identical.
export const MemoRow = memo(function Row({
name,
value,
}: RowProps) {
return <Text>{name}: {value}</Text>;
});Custom Comparison FunctionSupply custom comparator evaluating only specific keys.
function arePropsEqual(prev: Props, next: Props) {
return prev.user.id === next.user.id;
}
export const UserCard = memo(CardView, arePropsEqual);Display Name for DevToolsAssign displayName to retain debugging visibility.
MemoRow.displayName = 'MemoRow';Code Splitting with React.lazy
Dynamic Route SplittingSplit application bundle at route level.
const Settings = lazy(() => import('./Settings'));
return (
<Suspense fallback={<PageSpinner />}>
<Settings />
</Suspense>
);Modal on DemandLoad dialog chunk only when user clicks open.
const HeavyModal = lazy(() => import('./Modal'));
return (
<Suspense fallback={null}>
{isOpen && <HeavyModal onClose={close} />}
</Suspense>
);Named Export Import AdapterImport named export with intermediate resolution.
const Chart = lazy(() =>
import('./Charts')
.then((m) => ({ default: m.Chart }))
);React Compiler Architecture
Automatic MemoizationCompiler infers dependencies and memoizes build.
// No manual useMemo/useCallback required
function ProductList({ items }) {
const list = items.filter(isValid);
return <ListView items={list} />;
}Rules of React EnforcementCompiler requires strict adherence to hook rules.
// Must keep render functions completely pure
// Never mutate existing variables in renderGradual AdoptionAdopt compiler incrementally across specific paths.
// babel.config.js
plugins: [['babel-plugin-react-compiler', options]];Memoization Pitfalls
Inline Object Prop BustInline object creation destroys React.memo benefits.
// BAD: new style object created every render
<MemoCard style={{ color: 'red' }} />Inline Callback Prop BustArrow function prop defeats equality comparison.
// BAD: new function reference created
<MemoButton onClick={() => doAction()} />Children Prop InvalidationPassing JSX children breaks shallow prop equality.
// BAD: JSX children always creates new object
<MemoCard><Text>Content</Text></MemoCard>Tips
- Always profile before wrapping components in
React.memo; comparing props incurs small overhead that can exceed cheap render costs. - Ensure props passed to
React.memocomponents maintain stable referential identity across renders usinguseMemoanduseCallback.
Warnings
- Passing inline functions or object literals into
React.memocomponents breaks memoization because new references are generated on every render. - Do not rely solely on manual memoization in new projects; prepare architecture for automated compilation via the
React Compiler.
In Practice
Construct an optimized data table row using React.memo and a custom comparison function that evaluates only entity ID.
- Declare Row component accepting complex product entity prop.
- Implement arePropsEqual comparator inspecting only product ID.
- Wrap component in React.memo with the custom comparator.
- Verify that parent re-renders skip row rendering when ID matches.
interface Item { id: string; name: string; }
function areEqual(prev: any, next: any) {
return prev.item.id === next.item.id;
}
export const MemoRow = memo(function Row({
item,
onSelect,
}: { item: Item; onSelect: (id: string) => void }) {
return (
<CardView onClick={() => onSelect(item.id)}>
<CardTitle>{item.name}</CardTitle>
</CardView>
);
}, areEqual);FAQ
React.memo is a higher-order component that wraps a component to prevent re-renders when props match, while useMemo memoizes a value inside a component.
The React Compiler automatically memoizes components, values, and callbacks at build time, largely rendering manual React.memo calls unnecessary.
React.memo fails when any prop reference changes, when internal component state updates, or when a consumed React Context value changes reference.