# Setup Tailwind Typescript

> Configure Tailwind CSS with TypeScript in a Next.js or React project. Covers installation, configuration, custom theme extensions, component patterns, and type-safe styling utilities. Use when adding Tailwind CSS to an existing TypeScript project, customizing the Tailwind theme for a project's design system, setting up type-safe component styling patterns, or configuring Tailwind plugins and extensions.

- Skill: `pjt222/setup-tailwind-typescript` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pjt222/setup-tailwind-typescript`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pjt222/setup-tailwind-typescript/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- License: MIT
- Author: pjt222 (https://skillmd.com/u/pjt222)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/pjt222/setup-tailwind-typescript

---


# Set Up Tailwind CSS with TypeScript

Configure Tailwind CSS in a TypeScript project with custom theme, utilities, and type-safe patterns.

## When to Use

- Adding Tailwind CSS to an existing TypeScript project
- Customizing Tailwind theme for a project's design system
- Setting up type-safe component styling patterns
- Configuring Tailwind plugins and extensions

## Inputs

- **Required**: TypeScript project (Next.js, Vite, or standalone React)
- **Optional**: Design system tokens (colors, spacing, fonts)
- **Optional**: Tailwind plugins to include

## Procedure

### Step 1: Install Tailwind CSS

```bash
npm install -D tailwindcss @tailwindcss/postcss postcss
```

For Next.js (if not already included):

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

**Expected:** `tailwindcss`, `postcss`, and `autoprefixer` are installed as dev dependencies. For Next.js, `tailwind.config.ts` and `postcss.config.js` are generated by `npx tailwindcss init -p`.

**On failure:** If `npx tailwindcss init` fails, install Tailwind first with `npm install -D tailwindcss` and retry. If using a monorepo, run the command from the app's root directory, not the workspace root.

### Step 2: Configure tailwind.config.ts

```typescript
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        primary: {
          50: "#eff6ff",
          100: "#dbeafe",
          500: "#3b82f6",
          600: "#2563eb",
          700: "#1d4ed8",
          900: "#1e3a5f",
        },
        secondary: {
          500: "#6366f1",
          600: "#4f46e5",
        },
      },
      fontFamily: {
        sans: ["Inter", "system-ui", "sans-serif"],
        mono: ["JetBrains Mono", "monospace"],
      },
      spacing: {
        "18": "4.5rem",
        "88": "22rem",
      },
    },
  },
  plugins: [],
};

export default config;
```

**Expected:** `tailwind.config.ts` has a `content` array matching the project's file locations, custom colors and fonts under `theme.extend`, and proper TypeScript typing with the `Config` import.

**On failure:** If custom classes do not render, verify the `content` paths match your actual directory structure. Paths are glob patterns relative to the project root. Missing paths mean Tailwind will not scan those files for class usage.

### Step 3: Set Up Global Styles

Edit `src/app/globals.css`:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  html {
    @apply antialiased;
  }

  body {
    @apply bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-100;
  }
}

@layer components {
  .btn-primary {
    @apply bg-primary-600 text-white px-4 py-2 rounded-lg
           hover:bg-primary-700 focus:outline-none focus:ring-2
           focus:ring-primary-500 focus:ring-offset-2
           transition-colors duration-200;
  }
}
```

**Expected:** `globals.css` contains the three Tailwind directives (`@tailwind base`, `@tailwind components`, `@tailwind utilities`) plus any custom base and component layer styles. The file is imported in the root layout.

**On failure:** If styles are not applied, verify `globals.css` is imported in `layout.tsx` (or `_app.tsx` for Pages Router). Check that the Tailwind directives are present and not commented out.

### Step 4: Create Type-Safe Utility Helpers

Create `src/lib/cn.ts`:

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

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

Install dependencies:

```bash
npm install clsx tailwind-merge
```

Usage in components:

```tsx
import { cn } from "@/lib/cn";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "primary" | "secondary" | "outline";
}

export function Button({ className, variant = "primary", ...props }: ButtonProps) {
  return (
    <button
      className={cn(
        "px-4 py-2 rounded-lg font-medium transition-colors",
        variant === "primary" && "bg-primary-600 text-white hover:bg-primary-700",
        variant === "secondary" && "bg-secondary-500 text-white hover:bg-secondary-600",
        variant === "outline" && "border border-gray-300 hover:bg-gray-50",
        className
      )}
      {...props}
    />
  );
}
```

**Expected:** `src/lib/cn.ts` exports a `cn()` function. `clsx` and `tailwind-merge` are installed as dependencies. Components use `cn()` to merge class names without conflicts.

**On failure:** If `clsx` or `tailwind-merge` are not found, run `npm install clsx tailwind-merge`. If TypeScript reports type errors in `cn.ts`, verify the `ClassValue` type is imported from `clsx`.

### Step 5: Add Dark Mode Support

Update `tailwind.config.ts`:

```typescript
const config: Config = {
  darkMode: "class", // or "media" for system preference
  // ... rest of config
};
```

Toggle implementation:

```tsx
"use client";
import { useEffect, useState } from "react";

export function ThemeToggle() {
  const [dark, setDark] = useState(false);

  useEffect(() => {
    document.documentElement.classList.toggle("dark", dark);
  }, [dark]);

  return (
    <button onClick={() => setDark(!dark)}>
      {dark ? "Light" : "Dark"} Mode
    </button>
  );
}
```

**Expected:** Dark mode toggles correctly between light and dark themes. The `dark` class is applied to the `<html>` element, and `dark:` prefixed utility classes respond accordingly.

**On failure:** If dark mode does not toggle, verify `darkMode: "class"` is set in `tailwind.config.ts`. Ensure the `dark` class is toggled on the `<html>` element (not `<body>`). For system-preference mode, use `darkMode: "media"` instead.

### Step 6: Add Plugins (Optional)

```bash
npm install -D @tailwindcss/typography @tailwindcss/forms
```

```typescript
// tailwind.config.ts
import typography from "@tailwindcss/typography";
import forms from "@tailwindcss/forms";

const config: Config = {
  // ...
  plugins: [typography, forms],
};
```

**Expected:** Plugins are installed as dev dependencies and registered in the `plugins` array of `tailwind.config.ts`. Plugin-provided classes (e.g., `prose` from typography, styled form elements from forms) are available in components.

**On failure:** If plugin classes do not render, verify the plugin is both installed (`npm ls @tailwindcss/typography`) and added to the `plugins` array. Restart the dev server after config changes.

## Validation

- [ ] Tailwind classes render correctly in the browser
- [ ] Custom theme values (colors, fonts, spacing) work
- [ ] `cn()` utility merges classes without conflicts
- [ ] Dark mode toggles correctly
- [ ] TypeScript shows no errors in config or components
- [ ] Production build purges unused styles

## Common Pitfalls

- **Content paths missing**: If classes don't render, check `content` array in config matches your file locations
- **Class conflicts**: Use `tailwind-merge` (via `cn()`) to prevent conflicting utility classes
- **Custom values not working**: Ensure custom values are under `extend` (to add) not at theme root (which replaces defaults)
- **Dark mode not toggling**: Check `darkMode` setting and that the `dark` class is on `<html>` not `<body>`

## Related Skills

- `scaffold-nextjs-app` - project setup before Tailwind configuration
- `deploy-to-vercel` - deploy the styled application

