Building Reusable and Styled React Components with Tailwind CSS: Inspired by Shadcn/ui

Building Reusable and Styled React Components with Tailwind CSS: Inspired by Shadcn/ui

Master the art of component-based UI development using Tailwind variants for efficiency and style.

Shadcn/ui has become a popular choice for building UIs in React projects. a strength rests in a set of well-styled and reusable components that take advantage of Tailwind CSS. This blog post has shown you how to create identical components in your React apps, promoting consistency and maintainability.

Project Structure and Dependencies

  • Folder Structure: Organize your components within a dedicated directory, such as src/components/ui. This promotes a clear separation of concerns.

  • Dependencies: Install the necessary dependencies:

    1. tailwindcss: The core utility-first CSS framework.

    2. clsx: A utility for joining class names together.

    3. tailwind-merge (Optional): A helper function specifically designed for Tailwind CSS within clsx. (Consider using it for a more Tailwind-centric approach.)

Building a Reusable Button Component

Let's create a Button component similar to Shadcn/ui's implementation:

// button.tsx

// Import necessary functions
import { cn } from "@/utils/cn";
import { cva } from "class-variance-authority";

// Define Button component props type
type ButtonProps = React.HTMLAttributes<HTMLButtonElement> & {
  variant?:
    | "default"
    | "destructive"
    | "outline"
    | "secondary"
    | "ghost"
    | "link";
  size?: "default" | "sm" | "lg" | "full" | "icon";
};

// Button component
const Button = ({ variant, size, className, ...props }: ButtonProps) => {
  return (
    <button
      {...props}
      className={cn(buttonVariants({ variant, size, className }))} 
    />
  );
};

// Define base styles and variants for the button
const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-black disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-black text-white shadow hover:bg-black/90",
        destructive: "bg-red-500 text-white shadow-sm hover:bg-red-500/90",
        outline:
          "border bg-transparent shadow-sm hover:bg-gray-100 hover:text-gray-800",
        secondary: "bg-gray-200 text-black shadow-sm hover:bg-gray-200/80",
        ghost: "hover:bg-gray-200 hover:text-gray-800",
        link: "text-black 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",
        full: "w-full h-9 px-4 py-2",
        icon: "h-9 w-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

export default Button;

Explanation

  • We define a Button component that accepts properties such as variation, size, and standard button attributes.

  • The cn function (explained later) combines classes based on their variation and size.

  • buttonVariants uses class-variance-authority (or a similar library) to create a base class and variants for various button types and sizes.

Note that this is a simplified example. You can extend it to include more customization props, such as the disabled status or icon rendering.

Utility Functions for Class Handling

// cn.ts

import clsx from "clsx";
import { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; // Optional

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

The cn function first spreads the props array into the clsx function. clsx then processes and concatenates these class values based on their truthiness. The result is then sent to twMerge, which appears to combine Tailwind CSS classes when the tailwind-merge library is used. Finally, the merged class names are returned.

Overall, this code snippet provides a utility function cn that can be used to concatenate and merge Tailwind CSS classes.

Conclusion

By following these steps and embracing the concepts of reusable components and Tailwind CSS variants, you can significantly enhance your React development experience.

Remember, this is just the beginning. Explore the vast Tailwind ecosystem for additional plugins and utilities to further streamline your component creation. With practice and a focus on reusability, you can build beautiful, maintainable, and scalable React applications using the power of Tailwind CSS.

Bye Bye👋