4 minutes
Mastering Ant Design v4: Custom Themes & Advanced Components
Summary
Ant Design v4 offers a rich suite of React components with deep theme customizability via Less variables, runtime theming through ConfigProvider, and a host of advanced components, virtualized tables, cascaders, and transfer lists, that solve complex UI needs. This post walks through theming strategies, dives into performance-friendly components, and builds a live-themed dashboard demo (ant.design, ant.design).
1. Introduction
Ant Design’s goal is to provide out-of-the-box, enterprise-grade UI building blocks. Version 4 refines this with improved style theming hooks and optimized component internals. Mastering its advanced features lets you prototype faster and ship scalable, performant web apps with a consistent look and feel.
2. Custom Theming
2.1 Overriding Less Variables
AntD’s design tokens (e.g., @primary-color, @font-size-base) live in Less files. To customize:
Create a
theme.lessfile:@primary-color: #1DA57A; @font-size-base: 16px; @border-radius-base: 8px;Configure your build (e.g., with
cracoor custom Webpack loader):// craco.config.js module.exports = { style: { less: { loaderOptions: { lessOptions: { modifyVars: require('./theme.less'), javascriptEnabled: true, }, }, }, }, };
This globally applies your brand colors and spacing scales without touching component code (ant.design).
2.2 Runtime Theming with ConfigProvider
Use ConfigProvider to switch themes or locales at runtime:
import { ConfigProvider, Button } from 'antd';
import enUS from 'antd/lib/locale/en_US';
function App({ theme }) {
return (
<ConfigProvider locale={enUS} theme={{ primaryColor: '#722ED1' }}>
<Button type="primary">Primary Button</Button>
</ConfigProvider>
);
}
You can pass a theme prop (in v5+; v4 uses Less overrides) and locale configs to all child components dynamically (ant.design).
3. Advanced Components
3.1 Virtualized Tables
For large datasets, combine AntD’s Table with rc-virtual-list:
import { Table } from 'antd';
import VirtualList from 'rc-virtual-list';
const columns = [{ title: 'Name', dataIndex: 'name' } /*...*/];
function VirtualTable({ data }) {
const [tableHeight, setTableHeight] = useState(400);
return (
<Table
columns={columns}
dataSource={data}
pagination={false}
components={{
body: (rawData) => (
<VirtualList
data={rawData}
height={tableHeight}
itemHeight={50}
itemKey="key"
>
{(item) => <Table.Row record={item} />}
</VirtualList>
),
}}
/>
);
}
This approach renders only visible rows, slashing memory and improving scroll performance (ant.design).
3.2 TreeSelect & Cascader
Handle nested data and async loading:
import { TreeSelect, Cascader } from 'antd';
<TreeSelect
treeData={categories}
loadData={onLoadData}
treeCheckable
showCheckedStrategy={TreeSelect.SHOW_PARENT}
/>
<Cascader
options={countryList}
loadData={fetchStates}
changeOnSelect
/>
Use loadData for on-demand child loading, and treeCheckable or changeOnSelect for complex selection needs (ant.design, ant.design).
3.3 Transfer & Searchable Transfer
The Transfer component supports custom render and search:
import { Transfer, Input } from 'antd';
<Transfer
dataSource={users}
showSearch
filterOption={(input, item) =>
item.name.toLowerCase().includes(input.toLowerCase())
}
render={(item) => item.name}
/>
showSearch adds an Input box; filterOption lets you tailor search logic. Ideal for permission UIs or role assignments (ant.design).
4. Dynamic Forms & Validation
4.1 Form.List for Field Arrays
<Form>
<Form.List name="users">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name }) => (
<Space key={key}>
<Form.Item name={[name, 'firstName']} rules={[{ required: true }]}>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item name={[name, 'lastName']}>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Space>
))}
<Button onClick={() => add()}>Add User</Button>
</>
)}
</Form.List>
</Form>
Manage dynamic lists of inputs easily (ant.design).
4.2 Conditional Validation & Editable Tables
Use dependencies and custom rules:
<Form.Item
name="password"
rules={[{ required: true }]}
>
<Input.Password />
</Form.Item>
<Form.Item
name="confirm"
dependencies={["password"]}
rules={[
{ required: true },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject('Passwords do not match');
},
}),
]}
>
<Input.Password />
</Form.Item>
Combining forms and Table with editable cells unlocks inline editing patterns in management UIs (ant.design).
5. Layout & Grid System
5.1 Responsive Grid
Control layout at breakpoints:
<Row gutter={[16, 16]}>
<Col xs={24} sm={12} md={8} lg={6} xl={4}>
<Card>Item</Card>
</Col>
{/* ... */}
</Row>
gutter sets spacing; xs–xl adjust spans per device (ant.design).
5.2 Layout Components
<Layout>
<Header>My App</Header>
<Layout>
<Sider collapsible>Menu</Sider>
<Content>Main Content</Content>
</Layout>
</Layout>
Sider supports collapsible and controlled collapse callbacks for side menus (ant.design).
6. Performance Optimizations
6.1 On-Demand Component Imports
Use babel-plugin-import to import only used components and styles:
// .babelrc
{
"plugins": [
["import", { "libraryName": "antd", "style": true }]
]
}
This tree-shakes unused modules and compiles Less to CSS per import (ant.design).
6.2 Webpack Chunk Splitting
Split AntD into theme and component bundles:
optimization: {
splitChunks: {
cacheGroups: {
antd: {
test: /[\\/]node_modules[\\/]antd[\\/]/,
name: 'antd',
chunks: 'all',
},
},
},
},
This lets the browser cache framework code separately from app logic.
7. Conclusion & Further Reading
Ant Design v4’s theming and advanced components empower you to build polished, performant UIs at scale. From global style tokens to dynamic, virtualized data views, mastering these features accelerates development and enhances UX.
Further Reading
- Customize Theme: https://ant.design/docs/react/customize-theme
- Component API: https://ant.design/components/overview
- Use with CRA: https://ant.design/docs/react/use-with-create-react-app