はじめに
業務アプリを作るとき、UIコンポーネントをどうするかって結構悩むんですよね。デザインセンスがない自分にとっては特に。
そんな中で出会ったのがAnt Design。中国のAlibaba系列のAnt Groupが開発しているReact UIライブラリです。
正直なところ、最初は「中国製か...」と少し身構えていたんですけど、使ってみたら「これ、めちゃくちゃ実用的じゃん」となりました。GitHubで97,000スター以上、NPM月間ダウンロード900万件以上という数字も納得の完成度です。
Ant Designとは
Ant Designは「An enterprise-class UI design language and React UI library」、つまりエンタープライズ向けのUIデザイン言語とReactコンポーネントライブラリです。
単なるUIコンポーネント集ではなく、デザインシステムとして体系化されているのが特徴。ボタンの色からスペーシング、アニメーションまで、一貫したルールに基づいて設計されています。
最新バージョンは5.x系で、MITライセンスのオープンソース。2,340人以上のコントリビューターが開発に参加していて、11年以上の歴史があります。
特徴・メリット
1. コンポーネントの豊富さが異常
これ、意外と他のライブラリと比較すると差がわかるんですけど、Ant Designはコンポーネントの数が本当に多い。
Table、Form、DatePicker、TreeSelect、Cascaderなど、業務アプリで必要になりそうなものはほぼ揃っている。特にデータ入力系のコンポーネントが充実していて、複雑なフォームも組みやすいんですよ。
個人的には、Tableコンポーネントの完成度が特に高いと思います。ソート、フィルター、ページネーション、行選択など、業務アプリで必要な機能が最初から入っている。
2. デザインの一貫性
Ant Design Specという独自のデザイン仕様に基づいているので、コンポーネントを組み合わせても違和感がない。
配色、タイポグラフィ、スペーシング、アニメーションまで統一されているから、デザインセンスがなくても「それっぽい」UIが作れます。これ、30代エンジニアとしては本当に助かる。
3. TypeScript対応が手厚い
全コンポーネントがTypeScriptで書かれていて、型定義がしっかりしている。propsの補完が効くし、型エラーで事前にバグを防げます。
特にFormコンポーネントとの連携が優秀で、フォームの値の型推論がちゃんと効く。業務アプリで複雑なフォームを扱うときに、この型安全性は時短になる。
4. 国際化対応が標準装備
ConfigProviderで言語設定を切り替えるだけで、DatePickerの曜日表示とかTableの「No Data」メッセージとか、全部自動で切り替わります。
多言語対応が必要な業務アプリでは、この機能だけでも選ぶ価値があると思いますね。
5. エコシステムの充実
Ant Design ProというReactの管理画面テンプレート、ProComponentsという高機能コンポーネント集、Ant Design Chartsというチャートライブラリなど、周辺ツールが充実しています。
これらを組み合わせれば、管理画面なんかは爆速で作れます。コスパ的に、これはかなり大きい。
インストール方法
基本的なインストール
# npm
npm install antd
# yarn
yarn add antd
# pnpm
pnpm add antd
スタイルの読み込み
Ant Design 5.x以降は、CSS-in-JSを採用しているので、CSSファイルを個別にインポートする必要がなくなりました。コンポーネントをインポートするだけで、スタイルも自動的に適用されます。
// これだけでOK
import { Button } from 'antd';
Next.jsでの設定
Next.jsのApp Routerを使っている場合は、ちょっとした設定が必要です。
// app/layout.tsx
import { AntdRegistry } from '@ant-design/nextjs-registry';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ja">
<body>
<AntdRegistry>{children}</AntdRegistry>
</body>
</html>
);
}
# 追加のパッケージをインストール
npm install @ant-design/nextjs-registry
基本的な使い方
Buttonコンポーネント
import { Button, Space } from 'antd';
export function ButtonDemo() {
return (
<Space wrap>
<Button type="primary">Primary</Button>
<Button>Default</Button>
<Button type="dashed">Dashed</Button>
<Button type="text">Text</Button>
<Button type="link">Link</Button>
<Button type="primary" danger>Danger</Button>
</Space>
);
}
typeプロパティでスタイルを切り替える。直感的で分かりやすいですね。
サイズ指定とアイコン
import { Button, Space } from 'antd';
import { SearchOutlined, DownloadOutlined } from '@ant-design/icons';
export function ButtonWithIcon() {
return (
<Space wrap>
<Button type="primary" icon={<SearchOutlined />}>
検索
</Button>
<Button icon={<DownloadOutlined />} size="large">
ダウンロード
</Button>
<Button type="primary" loading>
処理中
</Button>
</Space>
);
}
loadingプロパティをtrueにするだけで、ローディング状態になるのが地味に便利。
Tableコンポーネント
業務アプリの主役といっても過言ではないTable。
import { Table, Tag, Space } from 'antd';
import type { ColumnsType } from 'antd/es/table';
interface DataType {
key: string;
name: string;
age: number;
status: string;
}
const columns: ColumnsType<DataType> = [
{
title: '名前',
dataIndex: 'name',
key: 'name',
sorter: (a, b) => a.name.localeCompare(b.name),
},
{
title: '年齢',
dataIndex: 'age',
key: 'age',
sorter: (a, b) => a.age - b.age,
},
{
title: 'ステータス',
dataIndex: 'status',
key: 'status',
render: (status: string) => (
<Tag color={status === '有効' ? 'green' : 'red'}>
{status}
</Tag>
),
filters: [
{ text: '有効', value: '有効' },
{ text: '無効', value: '無効' },
],
onFilter: (value, record) => record.status === value,
},
];
const data: DataType[] = [
{ key: '1', name: '田中太郎', age: 32, status: '有効' },
{ key: '2', name: '佐藤花子', age: 28, status: '有効' },
{ key: '3', name: '鈴木一郎', age: 45, status: '無効' },
];
export function TableDemo() {
return (
<Table
columns={columns}
dataSource={data}
pagination={{ pageSize: 10 }}
/>
);
}
ソート、フィルター、ページネーションが組み込みで使える。これを自前で実装しようとすると結構大変なんですよね。
Formコンポーネント
import { Form, Input, Button, Select, message } from 'antd';
interface FormValues {
username: string;
email: string;
department: string;
}
export function FormDemo() {
const [form] = Form.useForm<FormValues>();
const onFinish = (values: FormValues) => {
console.log('送信データ:', values);
message.success('登録が完了しました');
};
return (
<Form
form={form}
layout="vertical"
onFinish={onFinish}
style={{ maxWidth: 400 }}
>
<Form.Item
label="ユーザー名"
name="username"
rules={[{ required: true, message: 'ユーザー名を入力してください' }]}
>
<Input placeholder="ユーザー名" />
</Form.Item>
<Form.Item
label="メールアドレス"
name="email"
rules={[
{ required: true, message: 'メールアドレスを入力してください' },
{ type: 'email', message: '有効なメールアドレスを入力してください' },
]}
>
<Input placeholder="email@example.com" />
</Form.Item>
<Form.Item
label="部署"
name="department"
rules={[{ required: true, message: '部署を選択してください' }]}
>
<Select placeholder="部署を選択">
<Select.Option value="engineering">開発部</Select.Option>
<Select.Option value="sales">営業部</Select.Option>
<Select.Option value="hr">人事部</Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" block>
登録
</Button>
</Form.Item>
</Form>
);
}
rulesでバリデーションを宣言的に書けるのが楽。エラーメッセージの表示も自動でやってくれます。
実践的なユースケース
テーマのカスタマイズ
Ant Design 5.xでは、ConfigProviderでテーマをカスタマイズできます。
import { ConfigProvider, Button, Card } from 'antd';
export function ThemedApp() {
return (
<ConfigProvider
theme={{
token: {
colorPrimary: '#1677ff',
borderRadius: 8,
fontFamily: '"Noto Sans JP", sans-serif',
},
components: {
Button: {
colorPrimary: '#722ed1',
},
},
}}
>
<Card title="カスタムテーマ">
<Button type="primary">カスタムボタン</Button>
</Card>
</ConfigProvider>
);
}
CSS変数も使えるので、ダークモード対応も比較的簡単に実装できます。
日本語化
import { ConfigProvider } from 'antd';
import jaJP from 'antd/locale/ja_JP';
export function App() {
return (
<ConfigProvider locale={jaJP}>
{/* アプリケーション全体 */}
</ConfigProvider>
);
}
これだけで、DatePickerの曜日とか、Tableの「データがありません」とか、全部日本語になります。地味だけど、これがあるとないとでは全然違う。
モーダルダイアログ
import { Modal, Button, Form, Input } from 'antd';
import { useState } from 'react';
export function ModalDemo() {
const [isOpen, setIsOpen] = useState(false);
const [form] = Form.useForm();
const handleOk = async () => {
try {
const values = await form.validateFields();
console.log('フォームの値:', values);
setIsOpen(false);
form.resetFields();
} catch (error) {
console.log('バリデーションエラー:', error);
}
};
return (
<>
<Button type="primary" onClick={() => setIsOpen(true)}>
新規作成
</Button>
<Modal
title="ユーザー登録"
open={isOpen}
onOk={handleOk}
onCancel={() => setIsOpen(false)}
okText="登録"
cancelText="キャンセル"
>
<Form form={form} layout="vertical">
<Form.Item
label="名前"
name="name"
rules={[{ required: true, message: '名前を入力してください' }]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</>
);
}
通知とメッセージ
import { Button, Space, message, notification } from 'antd';
export function NotificationDemo() {
const showMessage = () => {
message.success('保存しました');
};
const showNotification = () => {
notification.info({
message: 'お知らせ',
description: '新しいバージョンがリリースされました。',
placement: 'topRight',
});
};
return (
<Space>
<Button onClick={showMessage}>メッセージ表示</Button>
<Button onClick={showNotification}>通知表示</Button>
</Space>
);
}
ユーザーへのフィードバックも、関数を呼ぶだけ。これは時短になりますね。
shadcn/uiやMaterial UIとの比較
正直なところ、どれを選ぶかはプロジェクトの性質による。
| 項目 | Ant Design | shadcn/ui | Material UI |
|---|---|---|---|
| コンポーネント数 | 非常に多い | 中程度 | 多い |
| 業務アプリ向け | 最適 | 普通 | 良い |
| カスタマイズ性 | テーマで対応 | 完全自由 | テーマで対応 |
| バンドルサイズ | 大きめ | 最小限 | 大きめ |
| 学習コスト | 中程度 | 低い | 中程度 |
| デザイン | 独自仕様 | Tailwind | Material Design |
個人的には、業務アプリ、特に管理画面を作るならAnt Design一択ですね。コンポーネントの完成度が違う。
一方で、マーケティングサイトやLPなら、shadcn/uiの方がカスタマイズしやすいかもしれない。
まとめ
Ant Designを使ってみて感じたこと:
- 開発速度: 業務アプリで必要なUIがほぼ揃っていて、実装が早い
- 品質: エンタープライズ向けなので、細かいところまで作り込まれている
- TypeScript: 型定義がしっかりしていて、開発体験が良い
- エコシステム: Pro ComponentsやChartsなど、拡張も充実
- コミュニティ: 11年の歴史と97,000スターは伊達じゃない
30代になって思うのは、「車輪の再発明」にどれだけ時間を使うかという話。業務アプリのUI開発なんて、正直、差別化ポイントじゃないんですよね。
そういう意味で、Ant Designは「UIは任せて、ビジネスロジックに集中しろ」というメッセージを感じます。Table、Form、DatePickerあたりを自前で実装しようとしたことがある人なら、この価値がわかるはず。
まだ使ったことがない人は、公式サイトのコンポーネント一覧を見てみてください。「こんなのもあるのか」という発見があると思いますよ。