一、安装Tailwind CSS及其支持

pnpm add -D @types/node
pnpm add -D tailwindcss @tailwindcss/vite

二、配置Tailwind CSS

vite.config.ts中导入tailwindcss依赖

import tailwindcss from "@tailwindcss/vite";

增加tailwindcss插件

plugins: [react(), tailwindcss()]

三、配置路径别名和tsconfig

tsconfig.json中添加Path别名

{
  "compilerOptions": {
    /* Path aliases */
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    /* 其他配置不变 */
  },
  /* 其他配置不变 */
}

vite.config.ts中添加别名

import path from "path";

export default defineConfig(async () => ({
  plugins: [react(), tailwindcss()],

  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },

  // 其他配置不变
}));

四、安装shadcn/ui依赖

pnpm add class-variance-authority clsx tailwind-merge lucide-react

五、配置shadcn/ui

新建 components.json

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": false,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "src/index.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

新建 src/lib/utils.ts

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

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

六、添加全局样式

新建 index.css

@import "tailwindcss";

@theme inline {
  --color-background: hsl(0 0% 100%);
  --color-foreground: hsl(240 10% 3.9%);
  --color-card: hsl(0 0% 100%);
  --color-card-foreground: hsl(240 10% 3.9%);
  --color-popover: hsl(0 0% 100%);
  --color-popover-foreground: hsl(240 10% 3.9%);
  --color-primary: hsl(240 5.9% 10%);
  --color-primary-foreground: hsl(0 0% 98%);
  --color-secondary: hsl(240 4.8% 95.9%);
  --color-secondary-foreground: hsl(240 5.9% 10%);
  --color-muted: hsl(240 4.8% 95.9%);
  --color-muted-foreground: hsl(240 3.8% 46.1%);
  --color-accent: hsl(240 4.8% 95.9%);
  --color-accent-foreground: hsl(240 5.9% 10%);
  --color-destructive: hsl(0 84.2% 60.2%);
  --color-destructive-foreground: hsl(0 0% 98%);
  --color-border: hsl(240 5.9% 90%);
  --color-input: hsl(240 5.9% 90%);
  --color-ring: hsl(240 10% 3.9%);
  --color-chart-1: hsl(12 76% 61%);
  --color-chart-2: hsl(173 58% 39%);
  --color-chart-3: hsl(197 37% 24%);
  --color-chart-4: hsl(43 74% 66%);
  --color-chart-5: hsl(27 87% 67%);
  --radius-sm: calc(0.25rem - 2px);
  --radius-md: calc(0.25rem - 1px);
  --radius-lg: 0.25rem;
  --radius-xl: calc(0.25rem + 1px);
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}

main.tsx中导入 index.css

import "./index.css";

七、测试配置

项目打包构建

pnpm build

创建一个简单的shadcn/ui组件示例来验证配置

pnpm dlx shadcn@latest add button

更新 App.tsx使用shadcn组件

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import { Button } from "@/components/ui/button";

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");

  async function greet() {
    setGreetMsg(await invoke("greet", { name }));
  }

  return (
    <main className="min-h-screen flex flex-col items-center justify-center p-8">
      <div className="max-w-md w-full space-y-8">
        <div className="text-center space-y-2">
          <h1 className="text-3xl font-bold">Welcome to Tauri + React + shadcn/ui</h1>
          <p className="text-muted-foreground">
            Click on the logos to learn more
          </p>
        </div>

        <div className="flex justify-center gap-4">
          <a href="https://vite.dev" target="_blank">
            <img src="/vite.svg" className="h-12 w-12" alt="Vite logo" />
          </a>
          <a href="https://tauri.app" target="_blank">
            <img src="/tauri.svg" className="h-12 w-12" alt="Tauri logo" />
          </a>
          <a href="https://react.dev" target="_blank">
            <img src={reactLogo} className="h-12 w-12" alt="React logo" />
          </a>
        </div>

        <form
          className="space-y-4"
          onSubmit={(e) => {
            e.preventDefault();
            greet();
          }}
        >
          <div className="space-y-2">
            <input
              id="greet-input"
              className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-xs transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
              onChange={(e) => setName(e.currentTarget.value)}
              placeholder="Enter a name..."
            />
          </div>
          <Button type="submit" className="w-full">
            Greet
          </Button>
        </form>

        {greetMsg && (
          <div className="p-4 rounded-md bg-secondary">
            <p className="text-sm">{greetMsg}</p>
          </div>
        )}
      </div>
    </main>
  );
}

export default App;

启动Tauri桌面应用

pnpm tauri dev