Search code examples
reactjstypescriptnext.jstailwind-cssshadcnui

How to properly style shadcn/ui library Button component?


I'm working on project using shadcn/ui library. How do I customize it properly for my needs? Let's say, I need extra large red rounded Button in my project for CTA. What are the best practice?

Here is the shadcn/ui <Button> component:

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-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-300",
  {
    variants: {
      variant: {
        default: "bg-zinc-900 text-zinc-50 hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90",
        destructive:
          "bg-red-500 text-zinc-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90",
        outline:
          "border border-zinc-200 bg-white hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
        secondary:
          "bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80",
        ghost: "hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
        link: "text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50",
      },
      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 }
  • Do I need to write a new component on top of this, like that?
import React from 'react';
import { Button } from '@/components/ui/shadcn/button';

type StyledButtonProps = React.ComponentProps<typeof Button>

function StyledButton({...props }: StyledButtonProps) {
    return (
        <Button {...props} className='bg-red-500'>
            {props.children}
        </Button>
    );
}

export default StyledButton;
  • Or I can just add new variant and size in already existed component?
  • Or there is some other way to do that?

I've tried:

  • to add new variant and size in already existed component.
  • And create a new component on top of shadcn/ui component.

Solution

  • You could consider using the existing destructive variant with a new xl size:

    const buttonVariants = cva(
      "…",
      {
        variants: {
          // …
          size: {
            // …
            xl: 'h-14 rounded-2xl p-10' // Adjust classes to taste
          },
        },
        // …
      }
    )
    

    And the use them like so:

    <Button variant="destructive" size="xl">
    

    It may be worth reading up on Class Variance Authority (CVA) to understand the workings of the cva() call.