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.less
file:@primary-color: #1DA57A; @font-size-base: 16px; @border-radius-base: 8px;
Configure your build (e.g., with
craco
or 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