Install MiniDev UI in React with Vite

Set up MiniDev UI in a React + Vite app: the Tailwind v4 Vite plugin, the @ path alias, shadcn init, the tokens stylesheet, registry components and dark mode.

To add MiniDev UI to a React app built with Vite, install Tailwind CSS v4 with the @tailwindcss/vite plugin, set the @/* path alias in both tsconfig files and vite.config.ts, run npx shadcn@latest init, and import the MiniDev tokens stylesheet after Tailwind. After that, npx shadcn@latest add https://ui.minidev.pro/r/button.json copies any component into src/components/ui.

Step 1: Create a React app

bash
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install

Step 2: Add Tailwind CSS v4

bash
npm install tailwindcss @tailwindcss/vite

Replace everything in src/index.css with one line. Tailwind v4 is configured in CSS, so there is no tailwind.config.js to create.

src/index.csscss
@import "tailwindcss";

Step 3: Set up the path alias and run shadcn init

The Vite template splits TypeScript config across files, and the shadcn CLI looks for the alias in the root tsconfig.json. Add baseUrl and paths to both tsconfig.json and tsconfig.app.json:

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

TypeScript paths only affect type checking. Vite needs the same alias to resolve imports, and it needs the Tailwind plugin:

bash
npm install -D @types/node
vite.config.tsts
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"),
    },
  },
})
bash
npx shadcn@latest init

Init detects Vite, writes components.json, creates src/lib/utils.ts with the cn helper, and adds a theme to src/index.css. The next step replaces that theme.

Step 4: Add the MiniDev tokens stylesheet

MiniDev components read semantic tokens like bg-surface, text-fg-muted and shadow-raised from one stylesheet, served at https://ui.minidev.pro/r/styles.css. Save it into src:

bash
curl -o src/minidev.css https://ui.minidev.pro/r/styles.css
src/index.csscss
@import "tailwindcss";
/* keep any other @import lines shadcn init added, such as tw-animate-css */
@import "./minidev.css";

Delete the :root, .dark and @theme inline blocks that init generated. They set --accent, --border and --ring to neutral values and, coming after the import, override MiniDev's tokens: the accent turns gray. MiniDev's stylesheet already maps every shadcn variable name, so stock shadcn components keep working.

The stylesheet starts with @custom-variant dark (&:is(.dark *));, so dark mode follows a dark class on <html>; delete the duplicate line from init. It sets font-sans to var(--font-geist-sans), which only Next.js defines for you. Load Geist and define the variables, or the page falls back to the browser's serif:

index.htmlhtml
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap" rel="stylesheet" />
<style>
  :root {
    --font-geist-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
    --font-geist-mono: "Geist Mono", ui-monospace, monospace;
  }
</style>

Optional: the stylesheet ships four materials. Hairline is the default; set data-material="glass", "metal" or "paper" on the <html> tag in index.html, or on any element, to restyle the page or one section without touching a component.

Step 5: Add components with the shadcn CLI

bash
npx shadcn@latest add https://ui.minidev.pro/r/button.json
npx shadcn@latest add https://ui.minidev.pro/r/pricing-plans.json

The first command writes src/components/ui/button.tsx and installs @base-ui/react and class-variance-authority. The second shows registry dependencies at work: pricing-plans depends on button, number-roll and segmented-control by full MiniDev URL, so the CLI fetches all of them and installs lucide-react and motion. Decline the prompt to overwrite button.tsx if you have edited it.

ButtonuiShadcn compatible React button on Base UI with eight variants, nine sizes including icon sizes, a pressed state and render prop support for links.npx shadcn@latest add ui.minidev.pro/r/button.jsonPricing PlansblockA React pricing section with a monthly or yearly switch that rolls prices to the new value, a featured ink plan, and feature rows marked included or not.npx shadcn@latest add ui.minidev.pro/r/pricing-plans.json

Step 6: Use it in a page

src/App.tsxtsx
import { Button } from "@/components/ui/button"
import { PricingPlans } from "@/components/ui/pricing-plans"

export default function App() {
  return (
    <main className="mx-auto max-w-5xl space-y-10 p-8">
      <Button variant="accent" onClick={() => alert("Hello")}>
        Start free
      </Button>
      <PricingPlans onSelect={(plan, period) => console.log(plan, period)} />
    </main>
  )
}

Run npm run dev. A Vite SPA renders everything on the client, so there are no server boundaries to think about: pass handlers straight in. For shadows that follow the pointer, add https://ui.minidev.pro/r/light-provider.json and render <LightProvider /> once in App.

Alternative: the npm package

bash
npm i minidev-ui-kit @base-ui/react class-variance-authority clsx tailwind-merge lucide-react motion
src/index.csscss
@import "tailwindcss";
@import "minidev-ui-kit/styles.css";
@source "../node_modules/minidev-ui-kit";

Import by path: minidev-ui-kit/ui/button, minidev-ui-kit/blocks/pricing-plans. Vite compiles the package's TypeScript source without extra config. @source is required because Tailwind does not scan node_modules; the path is relative to the CSS file. You can skip shadcn init on this route, though the font variables above still apply.

Dark mode without a flash

Set the class in index.html before the bundle loads. There is no server render, so no hydration warning to suppress:

index.htmlhtml
<head>
  <script>
    try {
      var t = localStorage.getItem("theme")
      var d = t ? t === "dark" : matchMedia("(prefers-color-scheme: dark)").matches
      if (d) document.documentElement.classList.add("dark")
    } catch (e) {}
  </script>
</head>

Your toggle then flips document.documentElement.classList and writes theme to localStorage.

Vite specific gotchas

  • The alias must exist in three places: tsconfig.json (for the CLI), tsconfig.app.json (for the editor and tsc) and vite.config.ts (for the bundler). Missing any one gives a different error.
  • Component files start with "use client". In a plain Vite app the directive does nothing. If a build prints that it was ignored, the warning is harmless.
  • Vite does not ship a font loader. Without --font-geist-sans defined, text renders in serif.

Troubleshooting

  • `Failed to resolve import "@/components/ui/button"`. The resolve.alias entry is missing from vite.config.ts.
  • shadcn init reports no import alias. Add paths to the root tsconfig.json, not only tsconfig.app.json.
  • Components render unstyled. src/index.css is not imported in src/main.tsx, or MiniDev is imported before Tailwind.
  • The accent is gray. A generated :root block is overriding the tokens. Delete it.

Frequently asked questions

Do I need the `@/*` alias if I use the npm package?

No. Package imports like minidev-ui-kit/ui/button resolve through node_modules. The alias is only needed for the shadcn CLI route, where components live in your src folder.

Does MiniDev UI work with the SWC React plugin?

Yes. @vitejs/plugin-react-swc works the same way; swap it into the plugins array.

Can I use MiniDev UI with Tailwind CSS v3 in Vite?

No. The stylesheet relies on v4 features such as @theme, @custom-variant and CSS-first configuration. Upgrade to v4 with the @tailwindcss/vite plugin first.

Can I use JavaScript instead of TypeScript?

Yes. Start from the react template instead of react-ts. With "tsx": false in components.json, the CLI converts each MiniDev file to .jsx as it installs. Put the alias in jsconfig.json instead of the tsconfig files.

Other frameworks