Performance Optimization Techniques in Ant Design: Memoization and Code Splitting

Ant Design (antd) leverages React.memo and useMemo to prevent unnecessary re-renders, and employs React.lazy with Suspense for code splitting to minimize initial bundle size.

Ant Design is a comprehensive React UI library used by millions of developers to build enterprise-grade applications. To ensure smooth performance across complex component trees and large datasets, the library implements modern React optimization patterns directly in its source code. Understanding these performance optimization techniques in Ant Design allows you to mirror these proven strategies in your own applications while staying fully compatible with the framework's internals.

Memoization Strategies in Ant Design

Memoization is fundamental to Ant Design's rendering architecture. The library uses shallow comparison techniques to guard both component renders and expensive calculations, ensuring that heavy UI operations do not bottleneck the main thread.

React.memo for Component-Level Optimization

Ant Design wraps pure functional components with React.memo to prevent re-renders when props remain unchanged. This pattern is critical for list items, form elements, and modal containers that receive stable but complex prop objects.

  • components/transfer/ListItem.tsx: Each list item in the Transfer component is memoized to avoid cascading re-renders when parent state changes.
  • components/radio/group.tsx: The RadioGroup component is exported with React.memo, ensuring that radio selections do not trigger unnecessary parent updates.
  • components/modal/useModal/index.tsx: The ElementsHolder container uses memoization to maintain stable references across modal instances, preventing reflows when managing multiple dialog windows.

By applying React.memo at the component boundary, Ant Design reduces the reconciliation cost for frequently rendered lists and tables.

useMemo for Derived Data and Style Objects

While React.memo guards the render step, useMemo guards the calculation step. Ant Design caches expensive computations including style objects, filtered datasets, and configuration transformations.

  • components/watermark/index.tsx: The Watermark component memoizes markStyle, targetElements, and watermarkContext objects to prevent recalculating CSS gradients and positioning on every render.
  • components/table/hooks/useSelection.tsx: Selection state logic uses useMemo to cache derived data for row selections, ensuring that checkbox calculations do not recompute when unrelated table data updates.
  • components/tree/Tree.tsx: Draggable configuration objects are memoized to maintain stable references for drag-and-drop event handlers across large tree structures.

This pattern prevents JavaScript from recreating objects and arrays on every render cycle, which is particularly important when passing these values as props to memoized child components.

Code Splitting with React.lazy and Suspense

To minimize the initial JavaScript payload, Ant Design implements route-based and component-based code splitting using dynamic imports. This strategy defers loading heavy utilities until they are actually required by the user.

The documentation site and internal demos utilize React.lazy combined with Suspense boundaries:

When the user navigates to these sections, Webpack loads the corresponding JavaScript chunk asynchronously, reducing the main bundle size and improving First Contentful Paint metrics.

Practical Implementation Examples

When building Ant Design applications, apply these same patterns to your custom components:

Memoizing List Items with React.memo

Mirror the Transfer component's approach by wrapping list items to prevent unnecessary updates:

import React from 'react';

interface ItemProps {
  label: string;
  selected: boolean;
  onClick: () => void;
}

// React.memo prevents re-render when label, selected, or onClick remain stable
const ListItem = React.memo(({ label, selected, onClick }: ItemProps) => (
  <div className={selected ? 'selected' : ''} onClick={onClick}>
    {label}
  </div>
));

export default ListItem;

Caching Style Objects with useMemo

Follow the Watermark component's pattern for expensive style computations:

import React from 'react';

const ExpensiveStyledBox = ({ color }: { color: string }) => {
  // Style object only recreated when color changes
  const boxStyle = React.useMemo(() => ({
    background: `linear-gradient(45deg, ${color}, #fff)`,
    padding: 16,
    borderRadius: 8,
  }), [color]);

  return <div style={boxStyle}>Styled Box</div>;
};

export default ExpensiveStyledBox;

Code Splitting Heavy Modals

Implement dynamic loading for rarely used components to reduce initial bundle size:

import React, { Suspense } from 'react';
import { Button } from 'antd';

const SettingsModal = React.lazy(() =>
  import('./SettingsModal') // Heavy modal with charts/forms
);

export default function Dashboard() {
  const [open, setOpen] = React.useState(false);
  
  return (
    <>
      <Button onClick={() => setOpen(true)}>Open Settings</Button>

      {open && (
        <Suspense fallback={<div>Loading…</div>}>
          <SettingsModal onClose={() => setOpen(false)} />
        </Suspense>
      )}
    </>
  );
}

Summary

  • Use React.memo for pure presentational components that receive stable props, particularly in lists and tables like those found in components/transfer/ListItem.tsx.
  • Apply useMemo to cache expensive calculations including style objects, filtered arrays, and configuration objects, as demonstrated in components/watermark/index.tsx and components/table/hooks/useSelection.tsx.
  • Implement code splitting with React.lazy and Suspense for heavy routes, modals, or utility components to reduce initial bundle size, following patterns in .dumi/theme/common/BehaviorMap/index.tsx.
  • Combine both techniques to maintain stable object references for props passed to memoized components, preventing shallow comparison failures.

Frequently Asked Questions

When should I use React.memo versus useMemo in an Ant Design application?

Use React.memo to wrap entire component definitions when you want to skip rendering if props haven't changed, which is ideal for list items and form inputs. Use useMemo inside components to cache specific values, calculations, or object definitions that would otherwise be recreated on every render. According to the Ant Design source code, these techniques are often used together: useMemo ensures stable prop references, while React.memo prevents the wrapped component from re-rendering.

Does code splitting affect SEO in Ant Design applications?

Code splitting with React.lazy primarily affects client-side rendered content that appears after user interaction, such as settings modals or dashboard widgets, which typically do not impact SEO. For content that must be indexed by search engines, ensure critical pages are either server-side rendered (SSR) or use static generation. The Ant Design documentation site uses lazy loading for interactive demos while keeping core content in the main bundle.

Can I memoize Ant Design's built-in components like Table or Form?

Ant Design's built-in components already implement internal memoization where appropriate, such as the RadioGroup and ListItem components. Rather than wrapping Ant Design components with React.memo, focus on memoizing your custom wrapper components and ensuring that callbacks and data arrays passed to Ant Design components maintain stable references using useMemo and useCallback.

What performance gains can I expect from memoization in large Ant Design tables?

For tables with thousands of rows or complex cell renderers, memoization can reduce render times by 50-80% during data updates, navigation, or state changes. By memoizing row components and derived selection data as seen in components/table/hooks/useSelection.tsx, you prevent the entire table from re-rendering when individual cell values update, significantly improving scrolling and interaction responsiveness.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →