antd
Ant Design is a large React component system aimed at data-heavy business applications. It supplies typed controls, forms, tables, navigation, overlays, date pickers, layouts, internationalization, icons, and a token-based CSS-in-JS theme layer with CSS variables. Version 6 requires React 18 or newer and modern browsers. It is more opinionated than a headless component kit: choosing it gives you a recognizable visual language and coordinated behavior, but deep visual departures require learning its token, semantic class, and style APIs.
One of the fastest ways to build a credible data-heavy React interface, provided the product can live within its design language. Skip it for small sites, old-browser support, or teams unwilling to own CSS-in-JS SSR and major-version migration work.
Use it if
- You are building an admin, operations, finance, or internal product that needs forms, tables, filters, modals, and navigation immediately
- Your team wants one documented React system with TypeScript types, accessibility behavior, locale packs, and consistent design tokens
- You can accept Ant Design's visual language or adapt it primarily through ConfigProvider tokens and component tokens
- You need a mature ecosystem around complex controls such as editable forms, tree selectors, date ranges, uploads, and virtualized tables
- You need a tiny marketing-site component layer: the full package measures 436.9 KB gzipped, has 47 runtime dependencies, and even tree-shaken use carries CSS-in-JS and component infrastructure
- Your project supports React 17, Internet Explorer, or older browsers: v6 requires React 18+ and its migration guide limits support to modern browsers with CSS variables
- Your design must look unrelated to Ant Design: tokens cover systematic changes, but extensive one-off overrides across many complex component states become a maintenance project
- You want headless primitives that leave markup and styling entirely yours: Ant Design owns DOM structure, behavior, and appearance, and v6 changed internal DOM in ways that can break selectors targeting implementation details
- You want an uneventful upgrade from v5 without audit work: v6 keeps many deprecated APIs temporarily but logs warnings, requires icons v6, removes the React 19 patch, and schedules the deprecated surface for removal in v7
Setup reality
Install `antd`, React 18 or later, and React DOM 18 or later. Types ship with the package, so do not install `@types/antd`. Basic component imports work without a global component stylesheet because v6 generates styles with CSS-in-JS and CSS variables; import `antd/dist/reset.css` only if you want Ant's opinionated global reset. Icons live in `@ant-design/icons`, and v6 requires icons 6 or later if you depend on them directly. The default date library is Day.js, so Moment-based values from old examples are wrong. Tree shaking works with normal named ESM imports, but importing the browser-wide distribution is explicitly discouraged and the full package measures 436.9 KB gzipped. The difficult part is server rendering. Next.js App Router projects should install `@ant-design/nextjs-registry` and wrap the body with `AntdRegistry` to inject first-screen styles; the official guide warns that dotted subcomponents such as `Select.Option` and `Typography.Text` are problematic there, so favor `options` and direct imports. Pages Router and custom SSR need a shared `@ant-design/cssinjs` 2.x cache plus style extraction, and duplicate cssinjs versions can break context. Static CSS extraction adds another build script and package. Theme customization belongs in `ConfigProvider` tokens or component tokens, not brittle selectors into internal DOM. Static `message`, `notification`, and `Modal` calls cannot see provider context such as theme and locale; use the App component with `App.useApp()` or the component hooks. Teams upgrading from v5 should first reach the latest v5, remove all console deprecations, upgrade explicit icons to v6, remove the React 19 patch, and inspect custom DOM-targeting CSS. There are no credentials or native builds, but the combination of SSR style plumbing, portals, locale choice, design tokens, and 47 runtime dependencies makes the first production setup substantially more involved than the one-line install suggests.
Patterns
Render basic controls with named importsrender-components
import { Button, DatePicker, Space } from 'antd'
export function Toolbar() {
return (
<Space>
<DatePicker placeholder="Due date" />
<Button type="primary">Create task</Button>
</Space>
)
}Normal named imports support tree shaking. Do not load the complete browser distribution for an application build.
Opt into Ant Design's global resetapply-global-reset
// app entry or root layout
import 'antd/dist/reset.css'
export function App({ children }) {
return children
}Component styles work without this import. reset.css changes global element defaults, so add it deliberately and test existing application CSS.
Set global and component design tokenscustomize-theme
import { ConfigProvider } from 'antd'
export function Providers({ children }) {
return (
<ConfigProvider
theme={{
token: { colorPrimary: '#5b3cc4', borderRadius: 6 },
components: { Button: { controlHeight: 40 } },
}}
>
{children}
</ConfigProvider>
)
}Prefer documented seed and component tokens over selectors that depend on Ant Design's internal DOM structure.
Combine dark and compact algorithmsenable-dark-theme
import { ConfigProvider, theme } from 'antd'
<ConfigProvider
theme={{
algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],
}}
>
<Dashboard />
</ConfigProvider>Algorithms derive a complete token map. Nest another ConfigProvider when only one section needs a different theme.
Apply a component localeset-locale
import { ConfigProvider } from 'antd'
import enGB from 'antd/locale/en_GB'
import dayjs from 'dayjs'
import 'dayjs/locale/en-gb'
dayjs.locale('en-gb')
<ConfigProvider locale={enGB}>
<App />
</ConfigProvider>ConfigProvider translates component text, while Day.js controls date locale details. Configure both for date pickers.
Build a typed validated formvalidate-form
import { Button, Form, Input } from 'antd'
type Values = { email: string }
export function InviteForm() {
const [form] = Form.useForm<Values>()
return (
<Form form={form} layout="vertical" onFinish={(values) => console.log(values)}>
<Form.Item name="email" label="Email" rules={[{ required: true }, { type: 'email' }]}>
<Input autoComplete="email" />
</Form.Item>
<Button htmlType="submit" type="primary">Invite</Button>
</Form>
)
}Form.Item controls its child when name is set. Use initialValues on Form rather than defaultValue on the nested Input.
Render a typed table with paginationrender-data-table
import { Table, type TableColumnsType } from 'antd'
type User = { id: string; name: string; role: string }
const columns: TableColumnsType<User> = [
{ title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.localeCompare(b.name) },
{ title: 'Role', dataIndex: 'role', filters: [{ text: 'Admin', value: 'admin' }], onFilter: (v, row) => row.role === v },
]
<Table<User> rowKey="id" columns={columns} dataSource={users} pagination={{ pageSize: 20 }} />Provide a stable rowKey. For server-side sort and pagination, handle onChange and avoid local sorter or onFilter functions.
Open a modal that receives provider contextopen-context-modal
import { App as AntApp, Button } from 'antd'
function DeleteButton() {
const { modal } = AntApp.useApp()
return (
<Button danger onClick={() => {
modal.confirm({
title: 'Delete record?',
content: 'This cannot be undone.',
onOk: deleteRecord,
})
}}>
Delete
</Button>
)
}
export function Screen() {
return <AntApp><DeleteButton /></AntApp>
}The top-level static Modal.confirm cannot read ConfigProvider theme, locale, or other React context. App.useApp can.
Show a message after an async actionshow-context-message
import { App as AntApp, Button } from 'antd'
function SaveButton() {
const { message } = AntApp.useApp()
const save = async () => {
const hide = message.loading('Saving', 0)
try {
await api.save()
hide()
message.success('Saved')
} catch (error) {
hide()
message.error('Save failed')
}
}
return <Button onClick={save}>Save</Button>
}Render an AntApp ancestor before calling useApp. Hook-based feedback follows provider theme and locale; static calls do not.
Use the options API for a searchable selectconfigure-select
import { Select } from 'antd'
<Select
showSearch
optionFilterProp="label"
placeholder="Choose owner"
options={users.map((user) => ({ label: user.name, value: user.id }))}
onChange={(userId) => setOwner(userId)}
/>The options API avoids deprecated child patterns and works better with Next.js App Router than dotted subcomponents such as Select.Option.
Build a responsive gridresponsive-layout
import { Col, Row } from 'antd'
<Row gutter={[16, 16]}>
<Col xs={24} md={16}><MainPanel /></Col>
<Col xs={24} md={8}><Sidebar /></Col>
</Row>The grid uses 24 columns. Responsive props change layout at Ant Design breakpoints, not container-query boundaries.
Inject first-screen styles in Next.js App Routernextjs-app-router-ssr
// app/layout.tsx
import { AntdRegistry } from '@ant-design/nextjs-registry'
export default function RootLayout({ children }: React.PropsWithChildren) {
return (
<html lang="en">
<body><AntdRegistry>{children}</AntdRegistry></body>
</html>
)
}Install @ant-design/nextjs-registry separately. Without style registration, server-rendered components can appear unstyled on the first screen and flicker after hydration.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @mui/material | npm | You want a similarly broad React system built around Material Design with a larger Western product footprint |
| @chakra-ui/react | npm | You prefer composable style props and a smaller set of application components over dense enterprise widgets |
| @mantine/core | npm | You want a broad typed React suite with hooks and flexible theming but less of a fixed enterprise visual identity |
| @arco-design/web-react | npm | You want another enterprise React suite with a similar component breadth and are willing to accept a smaller ecosystem |