Skip to content

How to Set Up shadcn/ui with Tailwind CSS v4 Using Vite

Written By Ajay Patel Categories: tutorials
Published: Updated:
11 min read

shadcn/ui configured with Tailwind CSS v4 in a React Vite project

Good components are expensive. You can spend weeks building accessible dialogs, dropdown menus, and form controls yourself, or install a traditional component library and inherit its design decisions.

By comparison, shadcn/ui offers a different approach. Its CLI copies the component source directly into your project, giving you accessible, styled components that you can edit from the moment they arrive. Pair that model with Tailwind CSS v4’s CSS-first configuration and Vite’s fast development server, and you get a setup that is quick to start and flexible enough to evolve with your product.

If this is your first time using the library, read our complete guide to using shadcn/ui for an overview of its component ownership model, registries, and customization workflow.

The only tricky part is the initial wiring. Tailwind CSS, the import alias, and the shadcn CLI need to be configured in the correct order. Miss one piece and init may stop before generating the files you need.

This guide walks through the complete setup for a React and TypeScript project, explains what every step does, and includes shortcuts and fixes for the most common errors.

Prerequisites

You need:

  • Node.js 20.19+ or 22.12+, as required by current Vite releases
  • npm, pnpm, Yarn, or Bun
  • A code editor and terminal

The command selector in each step supports npm, pnpm, Yarn, and Bun. Your selection is remembered across every command on the page.

The examples use Vite’s React and TypeScript template. JavaScript works too, but you will need a matching jsconfig.json alias before the shadcn CLI can resolve your project paths.

Setting Up shadcn/ui with Tailwind CSS v4 Using Vite

Follow these six steps in order to configure Vite, Tailwind CSS v4, and shadcn/ui correctly.

Step 1: Create the Vite project

Create a new React and TypeScript application:

npx create-vite@latest my-app --template react-ts

Move into the new project directory:

cd my-app

Install the project’s dependencies:

npm install

The selector uses the create-vite executable directly, so the template option works consistently across all four package managers.

If you already have a React Vite project, open its root directory and continue with Step 2.

Your initial project should look similar to this:

my-app/
├── public/
├── src/
│   ├── App.tsx
│   ├── index.css
│   └── main.tsx
├── package.json
├── tsconfig.app.json
├── tsconfig.json
└── vite.config.ts

Step 2: Install Tailwind CSS v4

Install Tailwind CSS and its first-party Vite plugin:

npm install tailwindcss @tailwindcss/vite

Open src/index.css, remove the starter styles, and replace them with:

@import "tailwindcss";

Replacing the file matches the official setup and prevents Vite’s starter styles from competing with your generated theme and application layout.

Tailwind CSS v4 does not require a tailwind.config.js file for this setup. Configuration and theme customization live primarily in CSS, and Tailwind automatically detects classes in your source files.

Do not start the shadcn CLI yet. The import alias must exist first.

Step 3: Configure the TypeScript path alias

shadcn/ui commonly generates imports such as:

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

The @ alias needs to point to the src directory. Vite’s TypeScript template splits configuration across multiple files, so add the alias to both tsconfig.json and tsconfig.app.json.

Update tsconfig.json:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Next, add the same alias inside the existing compilerOptions object in tsconfig.app.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Keep the other compiler options already generated by Vite. The shortened example only shows the properties you need to add.

The duplication is intentional. The root configuration helps tooling discover the project alias, while the app configuration ensures the editor and TypeScript compilation resolve imports used by the application.

Using JavaScript instead of TypeScript

If you created the project with --template react, add a jsconfig.json file at the project root:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

You still need the matching Vite alias from the next step because jsconfig.json helps your editor, not the runtime build.

Step 4: Configure Tailwind and the alias in Vite

Install the Node.js type definitions so TypeScript understands the path import and __dirname used in vite.config.ts:

npm install -D @types/node

Replace the contents of vite.config.ts with:

import path from "path";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

This configuration does two separate jobs:

  • tailwindcss() compiles Tailwind utilities through Vite.
  • resolve.alias tells Vite how to resolve imports beginning with @ during development and production builds.

The TypeScript path configuration from Step 3 is not enough on its own. TypeScript understands the alias for type checking and editor tooling, but Vite also needs a runtime resolution rule.

Step 5: Initialize shadcn/ui

Now run the CLI from the project root:

npx shadcn@latest init

The CLI performs preflight checks, detects Vite and Tailwind CSS, and prompts you for project options such as the component base, design preset, base color, CSS variables, icon library, and import aliases. You can accept the defaults or choose the options that match your design.

After initialization, the project will include files such as:

my-app/
├── components.json
└── src/
    ├── index.css
    └── lib/
        └── utils.ts

The exact generated CSS depends on the options and CLI version. In Tailwind CSS v4 projects, expect theme variables and Tailwind mappings in src/index.css rather than a JavaScript Tailwind configuration file.

The CLI also installs the dependencies required by the selected component setup and creates the cn utility in src/lib/utils.ts.

To understand every generated setting before changing it, see our complete components.json field guide.

Important: The base color and CSS variable strategy are initialization-time choices. You can edit the generated theme tokens later, but changing those fields in components.json does not regenerate existing CSS or components.

Step 6: Add your first component

Install the shadcn/ui button:

npx shadcn@latest add button

The component is copied into:

src/components/ui/button.tsx

This is a real source file in your application, not a component imported from an opaque package. You can inspect it, customize it, and keep it under version control.

Replace src/App.tsx with the following example:

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

function App() {
  return (
    <div className="flex min-h-svh items-center justify-center gap-2">
      <Button>Primary</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="outline">Outline</Button>
    </div>
  );
}

export default App;

Start the development server:

npm run dev

Open the local URL printed by Vite. Three visibly different, styled buttons confirm that:

  • The @ alias resolves correctly.
  • Tailwind CSS compiles your utility classes.
  • shadcn/ui’s generated theme is loaded.
  • The component import works.

Adding Multiple shadcn/ui Components

You can add several components with one command:

npx shadcn@latest add card dialog input label

To install every component from the default registry, use:

npx shadcn@latest add --all

The --all option is useful when exploring the registry, but it adds every available component and its dependencies to src/components/ui. For most production projects, adding components as you need them keeps the codebase easier to understand and maintain.

If you want more variations than the default registry provides, browse the shadcn ui components collection for individual controls or use complete shadcn blocks as starting points for larger interface sections. Because the source is added to your project, you can adapt each example to the theme and conventions established during setup.

Because the generated files belong to your repository, commit any custom component changes before running commands with --overwrite.

Faster Setup Options

If you are starting a completely new project, shadcn/ui offers two shortcuts that can replace the manual setup in Steps 1 through 5.

Build a preset visually with shadcn/create

Open shadcn/create, choose your component base, style, colors, fonts, icons, radius, and other options, and then copy the generated Vite command.

It will look similar to:

npx shadcn@latest init --preset [CODE] --template vite

Replace [CODE] with the preset code generated by shadcn/create. The exact command may also include options such as --base, --monorepo, or --rtl.

Scaffold a Vite project from the terminal

You can also let the shadcn CLI create the Vite project directly:

npx shadcn@latest init -t vite

Follow the prompts to choose the project name and configuration.

Whichever shortcut you use, components are added in the same way:

npx shadcn@latest add card

The manual route is still worth understanding because it is the process you need when adding shadcn/ui to a Vite application that already exists. For a framework-independent checklist, read How to Add shadcn/ui to an Existing Project.

Once the foundation is working, complete Shadcn templates can accelerate a new dashboard or landing page while preserving the same copy-and-customize workflow.

Monorepo projects

To scaffold a new monorepo, add the --monorepo flag:

npx shadcn@latest init -t vite --monorepo

When adding components from a monorepo root, use -c to target the application workspace when necessary:

npx shadcn@latest add card -c apps/web

The shadcn/ui Vite installation guide and CLI reference cover workspace targeting and the complete list of available commands.

How Tailwind CSS v4 Theming Works

Tailwind CSS v4 moves most configuration into CSS. After shadcn init, src/index.css typically contains several layers:

  • @import "tailwindcss" loads Tailwind CSS.
  • Additional imports load shadcn utilities and animations when required.
  • :root contains semantic values for the light theme.
  • .dark contains the dark-theme values.
  • @theme inline maps those values to Tailwind utilities such as bg-background and text-foreground.

A simplified token pair looks like this:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
}

Components use semantic utilities instead of hard-coded colors:

<div className="bg-background text-foreground">Content</div>

Changing the underlying CSS variables updates every component that uses the corresponding semantic token. You do not need a tailwind.config.js file for this standard Tailwind CSS v4 setup.

The Shadcn Theme Generator provides a visual way to experiment with these tokens before applying them to src/index.css. If your team defines the visual system before implementation, the Shadcn Figma library can keep design decisions aligned with the components developers use.

If you want to generate and reuse a complete visual configuration instead of editing every token manually, our guide to shadcn/ui presets explains the preset workflow.

Common Setup Errors and Fixes

The CLI cannot resolve the @/* alias

Check all three alias locations:

  1. tsconfig.json
  2. tsconfig.app.json
  3. vite.config.ts

The paths must point to the same directory. Restart the editor and development server after changing TypeScript or Vite configuration.

Cannot find module ‘path’ inside vite.config.ts

Install the Node.js type definitions:

npm install -D @types/node

Then confirm vite.config.ts imports path:

import path from "path";

Components render without styles

Confirm that src/index.css contains the Tailwind import:

@import "tailwindcss";

Also confirm src/main.tsx imports the stylesheet:

import "./index.css";

Finally, verify that tailwindcss() appears in the Vite plugins array.

The CLI cannot find the global CSS file

Open components.json and check the Tailwind CSS path:

{
  "tailwind": {
    "config": "",
    "css": "src/index.css"
  }
}

For Tailwind CSS v4, tailwind.config should remain an empty string. Update tailwind.css if you moved the stylesheet.

Utility classes work, but shadcn colors do not

This usually means Tailwind is running but the generated theme block is missing or was overwritten. Compare src/index.css with the output created by shadcn init, or rerun initialization after committing your current work so you can review the changes safely.

The page still shows Vite’s starter design

Remove the starter rules from src/index.css and any unused styles imported from src/App.css. Vite’s demo styles can constrain the root container, add unexpected spacing, or override the layout you are trying to build.

Frequently Asked Questions

Does shadcn/ui support Tailwind CSS v4?

  • Yes. The current shadcn CLI can initialize Tailwind CSS v4 projects and generates components, CSS variables, and utilities that use the v4 configuration model. New components also use Tailwind CSS v4-compatible styles.

Do I need a tailwind.config.js file?

  • No. A standard Tailwind CSS v4 and Vite project keeps its theme configuration in CSS. Leave tailwind.config empty in components.json and point tailwind.css to src/index.css.

Why does shadcn/ui require the @/* alias?

  • The alias gives generated components stable import paths such as @/lib/utils and @/components/ui/button. The CLI needs the alias to know where to write files, TypeScript needs it for type checking, and Vite needs it to resolve those imports at runtime.

Can I use JavaScript instead of TypeScript?

  • Yes. Start with Vite’s react template instead of react-ts, create the jsconfig.json alias shown earlier, and choose JavaScript when the shadcn CLI asks how components should be generated. The Vite runtime alias is still required.

Key Takeaways

  • Install and configure Tailwind CSS before running shadcn init in an existing project.
  • Configure the @/* alias in both TypeScript files and in vite.config.ts.
  • Use the official @tailwindcss/vite plugin with Tailwind CSS v4.
  • Keep the Tailwind config path empty for a standard Tailwind CSS v4 project.
  • Theme values live in CSS variables and are mapped through @theme inline.
  • shadcn/ui components are source files you own, not opaque package imports.
  • Add components as you need them to keep src/components/ui manageable.

Next Steps

  1. Add card, dialog, and input, then compose them into a small interface.
  2. Set up dark mode by toggling the dark class; the generated CSS already contains the dark-theme values.
  3. Open button.tsx, change a variant or class, and see how source ownership works.
  4. Swap a few :root theme values and watch every component using those tokens update.

Congratulations—shadcn/ui and Tailwind CSS v4 are now running in Vite!!! The setup work is complete, and each additional component is only one CLI command away.

For an editor-based workflow after setup, the shadcn/studio MCP server lets supported IDEs and AI tools explore components, blocks, and pages without leaving the development environment.


Resources

Happy building.