DEV Community

Cover image for Build Clean and Customizable Interfaces with Shadcn UI in React + TypeScript
Mohammed Fareedh
Mohammed Fareedh

Posted on • Originally published at Medium

Build Clean and Customizable Interfaces with Shadcn UI in React + TypeScript

When building modern front-end applications, developers often struggle to strike the perfect balance between accessibility, design flexibility, and developer experience. Shadcn UI offers a unique solution — combining the power of Radix UI primitives with Tailwind CSS in a developer-friendly and fully customizable package.

In this post, we’ll explore why Shadcn UI is gaining traction and walk through a step-by-step guide to set it up in a React + TypeScript project. We’ll also build a simple yet styled and accessible Button component using the library.


Why Use Shadcn UI?

Below are some compelling reasons why developers are turning to Shadcn UI:

1. Built with Radix UI Primitives

Shadcn UI leverages Radix UI for accessibility and behavior, ensuring components are built with accessibility best practices out of the box.

2. Full Control of Styling

Unlike traditional UI libraries, Shadcn UI doesn’t abstract styling away. You get full control with Tailwind CSS and can easily theme or extend components as needed.

3. Copy-Paste Component Model

Instead of a bloated npm package, Shadcn gives you the flexibility to copy only the components you need into your codebase — no unnecessary bundle size penalties.

4. TypeScript-First

Components are built with TypeScript, providing excellent type safety and IDE support.

5. Easily Maintainable and Customizable

Since the components live in your codebase, you can modify, optimize, and maintain them like any of your own custom components.


Setting Up Shadcn UI in a React + TypeScript Project

Let’s walk through integrating Shadcn UI into a new Vite + React + TypeScript project and using the Button component.

Step 1: Create a New Vite + React + TypeScript App

npm create vite@latest my-shadcn-app -- --template react-ts
cd my-shadcn-app
npm install
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

Edit your tailwind.config.ts:

import type { Config } from "tailwindcss";

const config: Config = {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

export default config;
Enter fullscreen mode Exit fullscreen mode

In src/index.css, add:

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

Step 3: Install and Initialize Shadcn UI

npm install shadcn@latest
npx shadcn@latest init
Enter fullscreen mode Exit fullscreen mode

During the prompt, you will be asked:

  • Which color would you like to use as the base color? — choose desired color

This generates a components folder with a utility-based setup.

Step 4: Add the Button Component

To install a Button component:

npx shadcn@latest add button
Enter fullscreen mode Exit fullscreen mode

This will create a reusable Button.tsx component inside your components/ui folder.

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-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
        destructive:
          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
        outline:
          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
        secondary:
          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
        ghost:
          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
        icon: "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Button({
  className,
  variant,
  size,
  asChild = false,
  ...props
}: React.ComponentProps<"button"> &
  VariantProps<typeof buttonVariants> & {
    asChild?: boolean
  }) {
  const Comp = asChild ? Slot : "button"

  return (
    <Comp
      data-slot="button"
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

export { Button, buttonVariants }
Enter fullscreen mode Exit fullscreen mode

Step 5: Use the Button in Your App

Update App.tsx:

import { Button } from "@/components/ui/button";

function App() {
  return (
    <div className="flex h-screen items-center justify-center bg-gray-100">
      <Button variant="default" onClick={() => alert("Shadcn Button Clicked")}>
        Click Me
      </Button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The variant="default" prop controls the button style, and you can customize or create your own variants by editing the component’s classNames.

Final Thoughts

Shadcn UI Library brings a new approach to component libraries: opinionated yet fully customizable, accessible yet unrestrictive. It gives developers control without compromising on design consistency or accessibility standards.

Whether you’re building an internal dashboard or a polished product UI, Shadcn UI is worth exploring — especially if you’re already using Tailwind CSS and want an ergonomic and maintainable component structure.

If you’re tired of bloated UI frameworks and want to own your UI code, Shadcn UI Library may be exactly what you need.

Top comments (0)