shadcn/ui Setup Guide

This document describes how shadcn/ui is configured in the LandlordX frontend application.

Overview

shadcn/ui is a collection of re-usable components built with Radix UI and Tailwind CSS. Unlike traditional component libraries, shadcn/ui components are copied directly into your project, giving you full ownership and control.

Configuration

Files

  • components.json: Main configuration file for shadcn/ui CLI
  • src/lib/utils.ts: Contains the cn() utility function for class merging
  • src/app/globals.css: CSS variables and theming configuration
  • tailwind.config.ts: Tailwind CSS configuration (minimal for v4)

Dependencies

The following packages are installed to support shadcn/ui:

  • class-variance-authority: For creating component variants
  • clsx: For conditional class names
  • tailwind-merge: For merging Tailwind classes without conflicts
  • lucide-react: Icon library used by shadcn/ui components
  • tailwindcss-animate: Animation utilities (dev dependency)

Usage

Adding Components

To add a new component from the shadcn/ui library:

# From the root directory
pnpm --filter frontend ui:add <component-name>
 
# Or from the frontend directory
pnpm ui:add <component-name>

Examples:

pnpm ui:add button
pnpm ui:add card
pnpm ui:add dialog
pnpm ui:add dropdown-menu

Using Components

After adding a component, import it using the @/ui alias:

import { Button } from "@repo/ui/button";
import { Card, CardHeader, CardTitle, CardContent } from "@repo/ui/card";
 
export default function MyPage() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Welcome</CardTitle>
      </CardHeader>
      <CardContent>
        <Button variant="default">Click me</Button>
        <Button variant="outline">Cancel</Button>
      </CardContent>
    </Card>
  );
}

Using the cn() Utility

The cn() utility function from @/lib/utils intelligently merges Tailwind CSS classes:

import { cn } from "@/lib/utils";
 
function MyComponent({ className, isActive }) {
  return (
    <div
      className={cn(
        "base-styles rounded-md p-4",
        isActive && "bg-primary text-primary-foreground",
        className,
      )}
    >
      Content
    </div>
  );
}

Theming

Light and Dark Mode

The application supports both light and dark themes. The theme is controlled by adding or removing the .dark class on the root element (typically <html>).

CSS variables are defined in src/app/globals.css:

  • Root variables (:root) define the light theme
  • Dark mode variables (.dark) define the dark theme

Customizing Colors

To customize the design system, edit the CSS variables in src/app/globals.css:

:root {
  --background: 0 0% 100%; /* HSL values */
  --foreground: 0 0% 3.9%;
  --primary: 0 0% 9%;
  /* ... other variables */
}
 
.dark {
  --background: 0 0% 3.9%;
  --foreground: 0 0% 98%;
  --primary: 0 0% 98%;
  /* ... other variables */
}

Radius

Border radius is controlled by the --radius variable:

:root {
  --radius: 0.5rem; /* Default radius */
}

This value is used throughout the components for consistent rounding.

Tailwind CSS v4 Integration

The project uses Tailwind CSS v4, which has a different syntax from v3:

  • Uses @import "tailwindcss" instead of @tailwind directives
  • Uses @theme directive for defining design tokens
  • CSS variables are mapped in the @theme block

The integration between shadcn/ui (which expects certain class names) and Tailwind v4 is handled by:

  1. Defining CSS variables in standard format (:root and .dark)
  2. Mapping them to Tailwind’s color system in the @theme block
  3. Using HSL color format for maximum flexibility

Component Customization

All shadcn/ui components are added to src/components/ui/ and become part of your codebase. This means:

  1. You own the code: Modify components directly to fit your needs
  2. No version lock-in: Components don’t update automatically
  3. Full customization: Change styles, behavior, or structure as needed
  4. Type-safe: All components are fully typed with TypeScript

Example of customizing a component:

// src/components/ui/button.tsx
// You can modify the variants, add new ones, or change default styles
 
const buttonVariants = cva("inline-flex items-center justify-center...", {
  variants: {
    variant: {
      default: "bg-primary text-primary-foreground...",
      // Add your custom variant
      brand: "bg-gradient-to-r from-blue-500 to-purple-500 text-white",
    },
    // ...
  },
});

Best Practices

  1. Use the cn() utility: Always use cn() for merging classes to avoid conflicts
  2. Keep components in ui/: Don’t modify the location of shadcn/ui components
  3. Use semantic color tokens: Use bg-primary instead of bg-gray-900 for consistency
  4. Leverage variants: Use the built-in variant props rather than overriding styles
  5. Test in both themes: Always check components in light and dark mode

Available Components

For a full list of available components, see the shadcn/ui documentation.

Popular components include:

  • Accordion
  • Alert Dialog
  • Avatar
  • Badge
  • Button
  • Card
  • Checkbox
  • Dialog
  • Dropdown Menu
  • Form
  • Input
  • Label
  • Select
  • Sheet
  • Table
  • Tabs
  • Toast
  • Tooltip

Troubleshooting

Component not found after adding

Make sure the import path uses the @/ui alias:

import { Button } from "@repo/ui/button"; // Correct
import { Button } from "@repo/ui/button"; // Avoid
import { Button } from "../components/ui/button"; // Avoid

Styles not applying

  1. Ensure globals.css is imported in your root layout
  2. Check that CSS variables are defined in both :root and .dark
  3. Verify Tailwind is processing your component files (check tailwind.config.ts content paths)

Dark mode not working

  1. Ensure the .dark class is toggled on the <html> element
  2. Verify dark mode variables are defined in globals.css
  3. Check that components use semantic color tokens (not hardcoded colors)

Resources