shadcn Vite Setup with Tailwind CSS v4 and React 19

Most shadcn Vite tutorials were written for Vite 5 and TypeScript 5, and two of their steps now break. The current Vite template ships TypeScript 6, which rejects baseUrl, and it type checks your code through tsconfig.app.json, so an alias added only to tsconfig.json never reaches it. This guide sets up a Vite, React 19 and Tailwind CSS v4 project that takes shadcn-style components, then installs them with the Wingo UI CLI or the shadcn CLI, with the trade-offs of each.
Everything below was run on October 13, 2026 with create-vite 9.2.1 (Vite 8.3.4, React 19.3.0, TypeScript 6.0.3), Tailwind CSS 4.3.3, the wingo-ui CLI 1.0.3, the shadcn CLI 4.21.4 and next-themes 0.4.6. For the Next.js version of this setup, read the Next.js component library setup guide.
What does a Vite project need for shadcn-style components?
Source-owned components are plain .tsx files that import each other through an alias, style themselves with Tailwind utilities and read colors from CSS variables. A Vite Tailwind v4 React project needs five things for them:
How do I do a shadcn Vite setup step by step?
1. Create the project and install Tailwind
The template already lists @types/node as of create-vite 9.2.1. Installing it again is harmless, and older templates need it for path in the config.
Replace everything in src/index.css with one line, and delete src/App.css along with its import in src/App.tsx. The starter CSS styles #root, h1 and body and would fight the components.
2. Add the plugin and the Vite path alias
This Vite path alias is for the bundler only. TypeScript does not read vite.config.ts, so it needs its own copy.
3. Set tsconfig.app.json paths, without baseUrl
The template's tsconfig.json has "files": [] and two references. Your src is checked through tsconfig.app.json. Add the alias to both:
Three details here, each from a failed run:
- Paths only in
tsconfig.jsonare not enough. We letwingo-ui initadd the alias on its own: it wrote it totsconfig.json, andtsc -bthen failed withCannot find module '@/components/ui/button', becausetsconfig.app.jsonnever saw it. - No
baseUrlon TypeScript 6. With"baseUrl": "."intsconfig.app.json,tsc -bstopped with error TS5101: the option is deprecated and stops working in TypeScript 7.pathsresolves relative to the config file without it. - Add
nodetotypes. The template limits global types tovite/client. Some components readprocess.env.NODE_ENVto print development warnings (Theme Toggle does), andtscreportsCannot find name 'process'without it. Vite replaces the expression when it builds: our production bundle had noprocess.envleft in it.
4. Run init
It detected vite, found the alias in tsconfig.app.json (the CLI follows the references list) and reported alias @/* -> src/. Then it created src/lib/utils.ts with cn, installed clsx and tailwind-merge, merged the tokens into src/index.css with a .dark block and @custom-variant dark (&:is(.dark *));, and wrote wingo-ui.json.
If package.json lists react-router or @react-router/dev, init records the framework as react-router instead of vite, because it checks for React Router first. File locations and commands stay the same.
5. Add components
The free items install without an account. Files land in src/components/ui, src/hooks and src/lib, with the npm packages they import. Give the page its colors in index.html, since the starter CSS that styled body is gone:
Then use them:
npm run build (which runs tsc -b first) passed with this file. Button, Card and Dialog carry "use client" at the top for the Next.js App Router; Vite ignores the directive. Press the button below to see its loading state.
$ npx wingo-ui@latest add buttonHow do I get dark mode in Vite without Next.js?
The components follow a .dark class on <html>. Something has to set it before the first paint, and something has to change it when the user picks a theme. Our Tailwind v4 dark mode guide covers the @custom-variant line those components rely on.
Before paint: a script in index.html, in <head>, reads the saved choice and the system setting:
On change: next-themes. Despite the name, version 0.4.6 depends on nothing but React, and it stores the choice under the same theme key the script reads:
We loaded the production build in Chromium with Playwright in both color schemes: the dark class was on <html> before React started when the system preferred dark, the theme toggle flipped it, and localStorage kept the choice. UIProvider is optional: it sets app-wide component defaults and the locale for dates and numbers.
What uses next-themes in the library? As of October 13, 2026, only theme-toggle.tsx imports it, and the CLI installs it with that item. Theme Toggle (Pro) also takes value and onValueChange if you would rather keep the theme in your own state; then your code sets the class. Three navigation components, Bounce Sidebar, Hook Sidebar and Gooey Nav, import next/link, so they need Next.js. Everything else runs in Vite unchanged.
Can I use the shadcn CLI in Vite instead?
Yes, with limits worth knowing first. We ran npx shadcn@latest init -t vite -b radix -p nova on a fresh project from step 3. Pick Radix: the installation docs note that Base UI styles rewrite asChild, which these components rely on. Init 4.21.4 wrote components.json, src/lib/utils.ts and its own src/components/ui/button.tsx.
Free Wingo UI items install from their URL:
The CLI asked to overwrite utils.ts and then button.tsx. Answer no to utils.ts: shadcn's version re-exports cn from the cn package, a drop-in for clsx plus tailwind-merge, and our files only need a cn export. Here is the catch. Keep shadcn's button and our dialog no longer type checks, because it passes props such as radius that shadcn's button does not have. Overwrite it and shadcn components that need its API break instead: shadcn's calendar imports buttonVariants, which our button does not export. The shadcn CLI also did not add our tokens to src/index.css, and it does not record versions, so wingo-ui update cannot merge later releases into those files.
For Pro items the docs add the @wingo-ui namespace to components.json with your key in an environment variable, then npx shadcn@latest add @wingo-ui/calendar. We did not test that path with a key for this post.
The setup that worked: one folder per library
In the same shadcn project we ran npx wingo-ui@latest init, which added only the 43 token names shadcn had not defined, changed "ui" in wingo-ui.json to "@/components/wingo", and ran npx wingo-ui@latest add dialog. Our files went to src/components/wingo, their imports were rewritten to match, shadcn's button.tsx stayed untouched, and tsc -b passed. The CLI docs cover the other aliases.
What should I check when something breaks?
Next step
Run npx wingo-ui@latest add button card dialog in your new project and open the installation docs for the manual path. The token system behind all of this is explained in the pillar on Tailwind v4 design tokens, and more setup guides live under Next.js and Tailwind CSS, including the Next.js component library setup if you move the project to the App Router.
FAQ
Does shadcn work with Vite?
Yes. shadcn components are React and Tailwind source files with no Next.js dependency. Vite needs the @tailwindcss/vite plugin, an @ alias in vite.config.ts and matching paths in tsconfig.app.json.
Why does tsc say it cannot find module @/components/ui/button in Vite?
The Vite template type checks src through tsconfig.app.json, which is a reference of tsconfig.json. Paths only in tsconfig.json do not reach it, so add the same paths entry for the @ alias, pointing at ./src, to tsconfig.app.json as well.
Should I add baseUrl to tsconfig.app.json?
Not on TypeScript 6, which the Vite template installs as of October 2026. tsc -b fails with error TS5101 because baseUrl is deprecated, and paths work without it.
Can I use next-themes in a Vite app?
Yes. next-themes 0.4.6 lists only react and react-dom as peer dependencies. Wrap the app in ThemeProvider with attribute="class" and add a small script to index.html so the class is set before the first paint.
Can I install Wingo UI components with the shadcn CLI in Vite?
Free items install from their URL, for example npx shadcn@latest add https://wingo-ui.com/r/dialog.json (opens in a new tab). The shadcn CLI does not add the Wingo UI tokens or record versions, and a shadcn-made button.tsx clashes with ours, so the Wingo UI CLI with its own folder is the smoother path.
- Vite
- shadcn/ui
- Setup
- Installation
- Tailwind CSS v4


