MUI vs Chakra UI vs shadcn/ui vs daisy UI ~React開発に最適なUIライブラリは?~
はじめに
Reactでアプリを作るとき、UIライブラリ選びって結構迷いますよね?👀
たくさんのオプションがある中で、どれが自分のプロジェクトに合うのか、正直悩むことも多いはずです。
そこで今回は、よく耳にする「MUI」「Chakra UI」「shadcn/ui」「daisy UI」をざっくり比較してみました!
MUI vs Chakra UI vs shadcn/ui vs daisy UI
まずはそれぞれを比較した表をご覧ください(9/17時点)。
MUI | chakra UI | shadcn/ui | daisy UI | |
---|---|---|---|---|
週間ダウンロード数 | 4,195,558 | 589,640 | - | 270,511 |
Last Publish | 6 days ago | 16 days ago | 2 days ago | 8 days ago |
Qiita | 195 | 156 | 29 | 48 |
Zenn | 185 | 121 | 120 | 20 |
ライブラリの種類 | (React) component UI library | (React) component UI library | Headless UI | component UI library |
Base | emotion(styled-components) | emotion | Radix UI×TailwindCSS | TailwindCSS |
componentの数 | ◎ | ◯ | ◯ | ◯ |
カスタマイズ性 | △ | ◯ | ◎ | △ |
その他 | 最も人気で、10年間安定している | chakraはナルトの『チャクラ』が由来 | JavaScriptエコシステムの中で最も注目されている | 公式ドキュメントが日本語に対応しており、ものすごく読みやすい |
シェア率
MUIが圧倒的でした。
shadcn/uiはnpm経由で利用することはないので、ダウンロード数は不明でした。
サポート&ドキュメント数
今のところ、コミュニティや開発者からのサポート面はどのライブラリも問題なさそうです。
ドキュメントはMUI、Chakra UIが豊富でした。
shadcn/uiはトレンドなので、これからどんどん増えていきそうです。
対してdaisy UIは少ないので公式ドキュメントを熟読する必要がありそうです。
MUI
MUIは最も人気なUIライブラリです。
コンポーネントはemotionでスタイルを構築されていますが、styled-componentsのスタイルエンジンに切り替えることもできます。
良い点
- Google's Material Designを標準で実装している。
- Reactのエコシステムの中で最も大きいコミュニティを持っている。
- 10年間継続的にメンテナンスされているため、安定性は抜群。
- 高性能で複雑なコンポーネントも使用できる(一部有料)。
悪い点
- カスタマイズがやりにくい。
- 本当に細かい箇所はMUIコンポーネント独自の長ったらしいクラス名を指定して、カスタマイズする必要がある。
- emotionをベースと使用しているため、パフォーマンスが少し悪い。
スタイルの方法によって、パフォーマンスが変わるらしい。
import Stack from "@mui/material/Stack";
import Button from "@mui/material/Button";
export const MuiButton = () => {
return (
<Stack spacing={2} direction="row">
<Button variant="text">Text</Button>
<Button
variant="contained"
sx={{
borderRadius: "30px",
}}
>
Contained
</Button>
<Button
variant="outlined"
sx={{
"&:hover": {
backgroundColor: "black",
},
}}
>
Outlined
</Button>
</Stack>
);
};
chakra UI
Chakra UIは、シンプルにモジュール化されたUIライブラリです。
MUIと同様、emotionがベースとなっております。
良い点
- MUIと比べてよりシンプルで柔軟性の高いコンポーネントを提供している。
- styleをpropsを用いてオーバーライドできるように設計されている。
悪い点
- emotionをベースと使用しているため、パフォーマンスが少し悪い。
import { Stack, Button } from "@chakra-ui/react";
export const ChakraButton = () => {
return (
<Stack spacing={3} direction="row" align="center">
<Button variant="ghost" colorScheme="teal">
TEXT
</Button>
<Button variant="solid" colorScheme="teal" borderRadius="30px">
CONTAINED
</Button>
<Button variant="outline" colorScheme="teal" _hover={{ bg: "#111111" }}>
OUTLINED
</Button>
</Stack>
);
};
shadcn/ui(シャドシーエヌ)
shadcn/uiはRadix UIとTailwind CSSをベースに開発された、Headless UIライブラリです。
Headless UIとは機能のみ提供するコンポーネントであり、見た目は開発者がカスタマイズします。
また、JavaScript ライジングスター 2023(年間のGitHubスターの増加数をランキングにしたサイト)の総合ランキングで1位になっており、最近のReactプロジェクトではかなり採用されているみたいです!
最も他のライブラリと異なる点は、npm経由でインストールを行わないところです。
代わりにCLIを通じてコードを取得します。
イメージがつきにくいと思うので、環境構築の手順を説明していきます。
- TailwindCSSの環境構築を行う
- 以下コマンドを実行し、質問に答えます。
$ npx shadcn-ui@latest init
1. Would you like to use TypeScript (recommended)? no/yes
TypeScriptを使いたいか?
2. Which style would you like to use? › Default
デフォルトかNew Yorkどちらのスタイルを使いたいか?
3. Which color would you like to use as base color? › Slate
ベースカラーは何にするか?(後から変更不可)
4. Where is your global CSS file? › › app/globals.css
グローバルCSSファイルはどこに置くか?
5. Do you want to use CSS variables for colors? › no / yes
CSS変数で色を設定するか?(後から変更不可)
6. Where is your tailwind.config.js located? › tailwind.config.js
tailwind.config.jsをどこに置くか?
7. Configure the import alias for components: › @/components
コンポーネントをインポートのエイリアスは?
8. Configure the import alias for utils: › @/lib/utils
utilsのimportのエイリアスは?
9. Are you using React Server Components? › no / yes
React Serverのコンポーネントを使うか?
- 使いたいコンポーネントをnpxで実行(コードをコピー)します。
今回はbuttonにします。
$ npx shadcn-ui@latest add button
- 3でコピーしたコンポーネントは
components/ui
(2を実行した時点で勝手にできてる)配下にbutton.tsx
として作成されます。
// components/ui/button.tsx
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button, buttonVariants };
なぜ、npmでインストールを行わないのでしょうか。
公式ではインストールを行わない理由について以下のように述べています。
この背後にある考え方は、あなたにコードの所有権と制御権を与え、コンポーネントの構築方法とスタイルを決定できるようにすることである。
まず、いくつかの理にかなったデフォルトを設定し、それからあなたのニーズに合わせてコンポーネントをカスタマイズしてください。
コンポーネントをnpmパッケージにすることの欠点の一つは、スタイルが実装と結合してしまうことです。コンポーネントのデザインは、実装とは切り離して考えるべきです。
npmでインストールするコンポーネントは既にスタイルが組み込まれているため、カスタマイズ性に欠けます。
そこで、shadcn/uiはコンポーネントをcomoponents/ui
配下に直接コピーすることで、スタイルを完全に開発者へ委ねます。
あなたが書いたコードということにしてあげるから、好きにしてねってことですね。
良い点
- カスタマイズ性に優れている
- TailwindCSSとの相性が良い
- コピーした時点で自分のコードとなるので、shadcnのバージョンを気にする必要がない(アップデートの影響を受けない)。
悪い点
- ファイルが増える
- 場合によってはディレクトリ構成の定義を考え直す必要が出てくる
- 意図しないライブラリ(icon等)が勝手にinstallされる
import { Button } from "./components/ui/button";
function App() {
return (
<div className="flex space-x-4">
<Button variant="ghost">TEXT</Button>
<Button className="rounded-[30px]">CONTAINED</Button>
<Button variant="outline" className="hover:bg-sky-200">
OUTLINE
</Button>
</div>
);
}
export default App;
daisy UI
daisy UIはTailwind CSSのプラグインとして、UIコンポーネントを提供しているライブラリです。
また、JavaScript ライジングスター 2023のCSSライブラリ部門ランキングで1位になっています(shadcnUIはCSSライブラリではないみたい)。
Tailwind CSSのプラグインなので、TailwindCSSでスタイルを書くようにコンポーネントを利用できます!
- daisyUIをインストール
npm i -D daisyui@latest
-
tailwind.config.js
ファイルにdaisyUI を追加します
module.exports = {
//...
plugins: [require("daisyui")],
}
- 使用したいコンポーネントをclassNameに記述する。
//btnと記述するだけでbuttonのコンポーネントを使用できる
<button className="btn">CONTAINED</button>
良い点
- TailwindCSSの環境下のあれば、どんなフレームワークでも使用可能
- TailwindCSSのユティリティクラスがすべて使用できる
- JavaScriptを必要としない
悪い点
- クラス名が長くなる
- サンプルが少ない
function App() {
return (
<div className="flex space-x-4">
<button className="btn btn-ghost">TEXT</button>
<button className="btn">CONTAINED</button>
<button className="btn btn-outline btn-accent">OUTLINED</button>
</div>
);
}
export default App;
React Hook Formとの相性
個人的にReact Hook Formをよく使用するので検証します。
MUI
基本的にはController
でラップする必要があります。
TextFieldはregister
でも問題なく動作しますが、サンプルとしてController
を使用しています。
import { useForm, Controller } from "react-hook-form";
import { TextField } from "@mui/material";
import Stack from "@mui/material/Stack";
import Button from "@mui/material/Button";
export const MuiForm = () => {
const { handleSubmit, control } = useForm<{ name: string }>({
mode: "onBlur",
defaultValues: {
name: "aiful",
},
});
const onSubmit = (data: { name: string }) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller<{ name: string }>
name="name"
control={control}
rules={{ required: "入力してください" }}
render={({ field, fieldState }) => {
return (
<TextField
{...field}
sx={{
marginBottom: "10px",
}}
error={fieldState.error ? true : false}
helperText={fieldState.error?.message}
/>
);
}}
/>
<Stack spacing={2} direction="row">
<Button
variant="outlined"
type="submit"
sx={{
"&:hover": {
backgroundColor: "black",
},
}}
>
SUBMIT
</Button>
</Stack>
</form>
);
};
Chakra UI
MUIと比べたら少しめんどくさいです。
Input
コンポーネントのprops
にhelperText
がないため、エラーメッセージ用にFormErrorMessage
というコンポーネントを使用する必要があります。
参考:Chakra UI + React Hook Form
import { useForm, Controller } from "react-hook-form";
import { Input, FormControl, FormErrorMessage } from "@chakra-ui/react";
import { Stack, Button } from "@chakra-ui/react";
export const ChakraButton = () => {
const { control, handleSubmit } = useForm<{ name: string }>({
mode: "onBlur",
defaultValues: {
name: "aiful",
},
});
const onSubmit = (data: { name: string }) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller<{ name: string }>
name="name"
control={control}
rules={{ required: "入力してください" }}
render={({ field, fieldState, formState }) => {
return (
//MUIと比べて若干記述量が多くなる
<FormControl isInvalid={formState.errors.name ? true : false}>
<Input {...field} marginY="10px" width="200px" />
<FormErrorMessage color="red">
{fieldState.error?.message}
</FormErrorMessage>
</FormControl>
);
}}
/>
<Stack spacing={3} direction="row" align="center">
<Button
variant="outline"
type="submit"
colorScheme="teal"
_hover={{ bg: "#111111" }}
>
OUTLINED
</Button>
</Stack>
</form>
);
};
shadcn/ui
shadcn/uiにはReact Hook Formを使用することを前提にしたコンポーネントがあります。
npx shadcn-ui@latest add form
これを使用してFormを構築しますが、shadcn/ui独自の書き方であるため学習コストがかかります。
参考:React Hook Form - Building forms with React Hook Form and Zod.
import { useForm } from "react-hook-form";
import { Button } from "./components/ui/button";
// formコンポーネント
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "./components/ui/form";
import { Input } from "./components/ui/input";
function App() {
const form = useForm<{ name: string }>({
mode: "onBlur",
defaultValues: { name: "aiful" },
});
const onSubmit = (data: { name: string }) => {
console.log(data);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
rules={{ required: "入力してください" }}
render={({ field, fieldState }) => (
<FormItem>
<FormControl>
<Input {...field} className="w-[200px] my-[10px]" />
</FormControl>
<FormMessage>{fieldState.error?.message}</FormMessage>
</FormItem>
)}
/>
<Button variant="outline" type="submit" className="hover:bg-sky-200">
OUTLINE
</Button>
</form>
</Form>
);
}
export default App;
daisy UI
普通に書くだけです。
Controller
を使用していないので、恐らくパフォーマンス(再レンダリングの面で)が最も良いです。
import { useForm } from "react-hook-form";
function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<{ name: string }>({
mode: "onBlur",
defaultValues: { name: "aiful" },
});
const onSubmit = (data: { name: string }) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col space-y-4 w-[200px]">
<input
type="text"
className="input border border-gray-300 my-[10px]"
{...register("name", {
required: "入力してください",
})}
/>
{errors.name && <p className="text-red-500">{errors.name.message}</p>}
<button type="submit" className="btn btn-outline btn-accent">
SUBMIT
</button>
</div>
</form>
);
}
export default App;
ライブラリを選定する際に考慮すべきこと
自分の考えを述べているだけなので、参考程度でお願いします。
細かいUXやデザインを求めているか
細かいUXやデザインを求めている場合、カスタマイズがしやすいshadcn/uiを使用すべきかなと思います。
逆に最低限のUXとデザインで良いのであれば、コンポーネントの完成度が高いMUIを使用すべきです。
パフォーマンスを気にするか
SSGであればCSS-in-JSでもbuild時にCSSファイルを生成するので、パフォーマンスに支障が出ないです。
しかし、選択肢としてSSRかCSRしかない場合(例えば認証機能を備えたプロダクト)はパフォーマンスが落ちます。
実際にChakra UIの公式では以下のように述べています。
Chakra UIはCSS-in-JSを使用しているため(emotion + styled-system)、この柔軟性は実行時に小さな代償を伴います。この実行時のフットプリントは、styled-system によるスタイルの計算と、emotion による className の生成によって発生します。
Chakraの使用を決定する前に、他の選択肢と比較することをお勧めします。しかし、ほとんどの小規模または中規模のデータ駆動型アプリケーションでは、Chakra UIは完璧にフィットすると考えています。
まとめ
どのUIライブラリを選ぶかは、プロジェクトの規模や要件、そしてチームの好みによって大きく変わります。
それぞれのライブラリには一長一短があるので、自分に合ったものを見つけることが大切です。
トレンドに敏感であればshadcn/uiに挑戦してみるのもアリですし、実績のあるMUIやChakra UIで安定を取るのも悪くありません。
最終的には、実際に試してみてフィットするものを選ぶのが一番ですね笑
ちなみに私はdaisy UI推しです💫
参考資料
Discussion