Next.jsにAnt Designを導入する方法

この投稿では、業務アプリの画面作りに便利なUIコンポーネントライブラリ「Ant Design」をNext.jsに導入する手順を説明します。言語はTypeScriptを用います。この手順では管理画面の雛形を作るところまでやります。

この手順で作るコードの完成形は[GitHub](https://github.com/suin/nextjs-ant-design-typescript/tree/1a62eaafb6a319eb96d6a24cfe39cd394061d392)で見れます。

[動作デモはこちら](https://nextjs-ant-design-typescript-rdsynl1ni-suin.vercel.app/)をご覧ください。


## Next.jsプロジェクトを生成する

まず次のコマンドを使ってNext.jsプロジェクトを作ります:

```bash
mkdir nextjs-ant-design-typescript
cd nextjs-ant-design-typescript
npx create-next-app --ts .
```

## Next.jsの開発サーバーを起動する

次のコマンドを実行して開発サーバーが起動できればOKです:

```bash
yarn dev
```

## Ant Designパッケージをインストールする

次に、Ant Designパッケージをインストールします。

```bash
yarn add antd
```

## 管理画面レイアウトを作る

管理画面のレイアウトを実装していきます。

layouts/adminLayout.tsxを作成し、その中身は次のようにします:

```tsx:layouts/adminLayout.tsx
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  UploadOutlined,
  UserOutlined,
  VideoCameraOutlined,
} from "@ant-design/icons";
import { Layout, Menu } from "antd";
import { ReactNode, useState } from "react";
import styles from "./adminLayout.module.css";

const { Header, Sider, Content } = Layout;

function AdminLayout({ children }: { readonly children: ReactNode }) {
  const [collapsed, setCollapsed] = useState<boolean>(false);

  function toggle() {
    setCollapsed(!collapsed);
  }

  return (
    <Layout>
      <Sider trigger={null} collapsible collapsed={collapsed}>
        <div className={styles.logo} />
        <Menu theme="dark" mode="inline" defaultSelectedKeys={["1"]}>
          <Menu.Item key="1" icon={<UserOutlined />}>
            nav 1
          </Menu.Item>
          <Menu.Item key="2" icon={<VideoCameraOutlined />}>
            nav 2
          </Menu.Item>
          <Menu.Item key="3" icon={<UploadOutlined />}>
            nav 3
          </Menu.Item>
        </Menu>
      </Sider>
      <Layout className="site-layout">
        <Header className={styles.siteLayoutBackground} style={{ padding: 0 }}>
          {collapsed ? (
            <MenuUnfoldOutlined className={styles.trigger} onClick={toggle} />
          ) : (
            <MenuFoldOutlined className={styles.trigger} onClick={toggle} />
          )}
        </Header>
        <Content
          className={styles.siteLayoutBackground}
          style={{
            margin: "24px 16px",
            padding: 24,
            minHeight: 280,
          }}
        >
          {children}
        </Content>
      </Layout>
    </Layout>
  );
}

export default AdminLayout;
```

管理画面レイアウト用のCSSモジュールもlayouts/adminLayout.module.cssに作ります:

```css:layouts/adminLayout.module.css
.trigger {
    padding: 0 24px;
    font-size: 18px;
    line-height: 64px;
    cursor: pointer;
    transition: color 0.3s;
}

.trigger:hover {
    color: #1890ff;
}

.logo {
    height: 32px;
    margin: 16px;
    background: rgba(255, 255, 255, 0.3);
}

.siteLayoutBackground {
    background: #fff;
}
```

## Ant DesignのグローバルCSSをimportする

Ant DesignのグローバルCSSをpages/_app.tsxにて`import`します:

```ts
import 'antd/dist/antd.css';
```

## 管理画面レイアウトをページで使う

今度は個別ページを作って、そこで管理画面レイアウトを使っていきます。

pages/index.tsxがコード生成で作られているので、その内容を次のコードにまるっと置き換えます:

```tsx
import dynamic from "next/dynamic";

const AdminLayout = dynamic(() => import("../layouts/adminLayout"), {
  ssr: false,
});

export default function Home() {
  return <AdminLayout>コンテンツ</AdminLayout>;
}
```

`dynamic`で`import`しているのは、Ant Designの`Layout`コンポーネントが依存するコンポーネントで`useLayoutEffect`が使われているためです。SSRを無効にする必要があります。次のissueが直るまでの対処法です。

- [Select mode="multiple" throws useLayoutEffect warning in Next.js · Issue #30396 · ant-design/ant-design](https://github.com/ant-design/ant-design/issues/30396)

次のような見た目になればOKです:

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889/cb6ecbf7-cc2e-6937-d05a-69707aaf4a25.png)