0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ShadCNで入るutils.tsって何をしているの?

Posted at

utils.ts

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

ShadCNを利用すると、上記Utilsファイルが入ってきます。このファイルの内容を知っていなくてもShadCN UIを利用できますが、解説していきます。

clsx

clsxは複雑な条件付きクラス名をいい感じに統合してくれるライブラリです。

import clsx from "clsx";

const className = clsx("text-base", "font-bold");
console.log(className);
// → "text-base font-bold"

基本的な使い方はこんな感じで、複数の文字列をスペース区切りで連結してくれます。

条件付きクラス

const isActive = true;
const className = clsx("btn", isActive && "btn-active");
console.log(className);
// → "btn btn-active"

こんな感じで、条件に応じてクラスを制御することも可能です。

twMerge

twMerge("bg-white", "bg-black") // → "bg-black"

twMergeはTailwind CSSのクラス名が衝突した時、後勝ちにして不要なクラスをマージしてくれます。

cn(...)

cn(...)はclsxとtwMergeの2つを組み合わせて、条件に応じてクラス名を構築、競合の解消を行うためのUtil関数になります。

cn("text-sm", isError && "text-red-500", "font-medium")
// isError = true → "text-sm text-red-500 font-medium"
// isError = false → "text-sm font-medium"

cn("bg-white", "bg-black") // → "bg-black"(マージされる)

上記のように条件によってクラス名を決定しながら、衝突したクラスを解消してくれています。

実例

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 gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline:
          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "h-9 w-9",
      },
    },
    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 }

上記は実際のShadCNのButtonコンポーネントです。
Buttonコンポーネントはクラス名を継承できる仕様になっており、

<Button className="bg-[#000000]" />

としたとき、cn(...)を嚙ますことで競合せず、後勝ちでクラス名を解決できます。

まとめ

ShadCNを利用していると意識することがない関数ですが、わかりやすく使いやすい関数であります。
コンポーネントを自作する際は使用するとコードがスマートになるので使用してみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?