A guide for front-end project setup and development based on the Vite + React + TailwindCSS v4 + lucide-react tech stack.
Tech Stack
| Technology | Version | Purpose |
|---|---|---|
| Vite | ^5.x or ^6.x | Build tool, development server |
| React | ^18.x or ^19.x | UI framework |
| TailwindCSS | ^4.x | Atomic CSS framework (Vite plugin mode) |
| @tailwindcss/vite | ^4.x | TailwindCSS Vite plugin |
| lucide-react | latest | Icon library |
| TypeScript | ^5.x or ^6.x | Type safety |
Project Initialization
Step 1: Create a Vite + React project
# 创建项目(使用 React + TypeScript 模板)
npm create vite@latest . -- --template react-ts
# 安装依赖
npm install
Step 2: Install TailwindCSS v4
# 安装 TailwindCSS v4 及 Vite 插件
npm install tailwindcss @tailwindcss/vite
Note: v4 no longer requires
postcss、autoprefixer, and there is no need to runnpx tailwindcss init。
Step 3: Configure the Vite plugin
In vite.config.ts add the @tailwindcss/vite plugin:
vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
})
Step 4: Clear the default styles and configure CSS (⚠️ mandatory key step)
You must clear all contents of src/index.css and src/App.css Clear all content of, then in src/index.css write only the TailwindCSS import (and optionally @theme):
src/index.css:
@import "tailwindcss";
src/App.css:
/* 清空此文件所有内容,或直接删除此文件 */
🚨 Strictly prohibited: Do not write any
index.cssin*、body、htmland other global selector styles! Including but not limited to:/* ❌ 以下全部禁止 */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: ...; -webkit-font-smoothing: antialiased; } html { scroll-behavior: smooth; }These global reset styles will override TailwindCSS's preflight (built-in reset), causing spacing, fonts, layout, and other styles to all become abnormal. TailwindCSS v4 already has a complete built-in CSS Reset,no additional global reset is needed or allowed。
The correct
index.csscontains only:@import "tailwindcss"+ optional@themecustom theme variables. Do not write any CSS rules other than these.
v4 uses
@import "tailwindcss"to replace v3's@tailwind base; @tailwind components; @tailwind utilities;. No longer needtailwind.config.jsconfiguration file.
Step 5: Install the lucide-react icon library
npm install lucide-react
Step 6: Install utility libraries (if a cn utility function is needed)
# 用于合并 className 的工具库
npm install clsx tailwind-merge
Utility function src/utils/cn.ts:
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Step 7: Start the development server
npm run dev
TypeScript Configuration (Important)
Key tsconfig.app.json configuration
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"verbatimModuleSyntax": false,
"isolatedModules": true,
"skipLibCheck": true
},
"include": ["src"]
}
⚠️ TypeScript pitfalls you must pay attention to
-
verbatimModuleSyntaxmust be set tofalse- When set to
true,import { MyType } from './types'will be retained as a runtime import, but the type does not exist at runtime, causing an error - If set to
true, then all type imports must use theimport type { MyType }syntax, but this is easy to miss
- When set to
-
Avoid component names having the same name as imported types
// ❌ 错误:TaskStats 类型和函数同名,导致 SyntaxError import { TaskStats } from '../../types'; export default function TaskStats(props: { stats: TaskStats }) { ... } // ✅ 正确:重命名类型导入 import type { TaskStats as TaskStatsData } from '../../types'; export default function TaskStats(props: { stats: TaskStatsData }) { ... } -
Import paths must be accurate
- Utility function
cnis defined inutils/cn.ts, do not import fromutils/helpers.tsImport - Each utility function should be imported from its correct file path
- Utility function
Development Standards
Project Structure
src/
├── components/ # 可复用组件
│ ├── ui/ # 基础 UI 组件(Button, Card, Input 等)
│ ├── layout/ # 布局组件(Header, Footer, Sidebar 等)
│ └── features/ # 业务功能组件
├── pages/ # 页面组件
├── hooks/ # 自定义 Hooks
├── utils/ # 工具函数
│ ├── cn.ts # className 合并工具(clsx + tailwind-merge)
│ └── helpers.ts # 业务工具函数
├── types/ # TypeScript 类型定义
├── mock/ # Mock 数据
│ └── data.ts # Mock API 数据
├── assets/ # 静态资源
├── App.tsx # 根组件
├── main.tsx # 入口文件
└── index.css # 全局样式(@import "tailwindcss")
Component Development Standards
import { useState } from 'react';
import { Search, Menu, X } from 'lucide-react';
interface HeaderProps {
title: string;
onMenuToggle?: () => void;
}
export function Header({ title, onMenuToggle }: HeaderProps) {
const [isOpen, setIsOpen] = useState(false);
return (
<header className="flex items-center justify-between px-6 py-4 bg-white shadow-sm">
<h1 className="text-xl font-bold text-gray-900">{title}</h1>
<div className="flex items-center gap-3">
<Search className="w-5 h-5 text-gray-500" />
<button
onClick={() => {
setIsOpen(!isOpen);
onMenuToggle?.();
}}
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
>
{isOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
</header>
);
}
Local Mock Data
Create src/mock/data.ts to simulate API data:
// src/mock/data.ts
export const mockUsers = [
{ id: 1, name: '张三', email: 'zhangsan@example.com', avatar: '' },
{ id: 2, name: '李四', email: 'lisi@example.com', avatar: '' },
];
// Mock API 函数
export async function fetchMockData<T>(data: T, delay = 500): Promise<T> {
return new Promise((resolve) => setTimeout(() => resolve(data), delay));
}
Common TailwindCSS patterns
{/* 响应式布局 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* 卡片 */}
<div className="bg-white rounded-xl shadow-md p-6 hover:shadow-lg transition-shadow">
<h3 className="text-lg font-semibold text-gray-900">标题</h3>
<p className="mt-2 text-gray-600">描述文字</p>
</div>
</div>
{/* 按钮样式 */}
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 active:bg-blue-800 transition-colors font-medium">
主按钮
</button>
{/* 输入框 */}
<input
type="text"
placeholder="请输入..."
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
/>
Icon usage
import {
Home, Settings, User, Bell, Search,
ChevronRight, ChevronDown, Plus, Trash2, Edit,
Check, X, AlertCircle, Info, Loader2
} from 'lucide-react';
// 使用图标
<Home className="w-5 h-5 text-gray-600" />
<Loader2 className="w-5 h-5 animate-spin" /> {/* 加载动画 */}
Self-test verification
After development is complete,muststart the development server and use agent-browser for self-testing:
# 1. 启动开发服务器(后台运行)
npm run dev &
# 2. 等待服务器就绪后,使用 agent-browser 打开页面
agent-browser open http://localhost:5173
# 3. 截取页面快照,检查元素是否正常渲染
agent-browser snapshot -i
# 4. 截图保存,供 QA 参考
agent-browser screenshot --full screenshot.png
# 5. 检查控制台是否有错误
agent-browser eval 'JSON.stringify(window.__errors || "no errors captured")'
# 6. 验证 TailwindCSS 样式是否生效
# 通过 getComputedStyle 检测常见 Tailwind class 是否正确应用
agent-browser eval 'JSON.stringify((() => {
const checks = [];
const q = (sel) => document.querySelector(sel);
const cs = (el) => el ? getComputedStyle(el) : null;
const flexEl = q(".flex");
if (flexEl) checks.push({ class: "flex", display: cs(flexEl).display, pass: cs(flexEl).display === "flex" });
const gridEl = q(".grid");
if (gridEl) checks.push({ class: "grid", display: cs(gridEl).display, pass: cs(gridEl).display === "grid" });
const bgEl = q("[class*=\"bg-\"]");
if (bgEl) checks.push({ class: bgEl.className.match(/bg-\S+/)?.[0], bg: cs(bgEl).backgroundColor, pass: cs(bgEl).backgroundColor !== "rgba(0, 0, 0, 0)" });
const roundedEl = q("[class*=\"rounded\"]");
if (roundedEl) checks.push({ class: "rounded", borderRadius: cs(roundedEl).borderRadius, pass: cs(roundedEl).borderRadius !== "0px" });
const paddingEl = q("[class*=\"p-\"], [class*=\"px-\"], [class*=\"py-\"]");
if (paddingEl) checks.push({ class: paddingEl.className.match(/p[xy]?-\S+/)?.[0], padding: cs(paddingEl).padding, pass: parseFloat(cs(paddingEl).paddingTop) > 0 || parseFloat(cs(paddingEl).paddingLeft) > 0 });
const allPass = checks.length > 0 && checks.every(c => c.pass);
return { tailwindActive: allPass, checksRun: checks.length, details: checks };
})())'
# 7. 验证响应式布局(模拟移动端)
agent-browser close
agent-browser --viewport 375x812 open http://localhost:5173
agent-browser screenshot --full mobile-screenshot.png
# 8. 关闭浏览器
agent-browser close
Self-test checklist:
- The page has no blank screen, and all components render properly
- The browser console has no SyntaxError / ReferenceError
- TailwindCSS styles take effect:
tailwindActive: true, and the computedStyle of classes such as flex/grid/bg/rounded/padding matches expectations - All interactive features work (click, input, filter, etc.)
- The responsive layout displays properly on mobile
- Icons display correctly
Build and preview
# 构建生产版本
npm run build
# 本地预览构建结果
npm run preview
Custom TailwindCSS theme
TailwindCSS v4 uses CSS @theme directives for theme customization, and no longer requires tailwind.config.js:
/* src/index.css */
@import "tailwindcss";
@theme {
--color-primary-50: #f0f9ff;
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
--font-sans: 'Inter', system-ui, sans-serif;
--font-display: 'your-display-font', sans-serif;
}
Using custom theme variables:
<div className="bg-primary-500 text-white font-display">品牌区域</div>
<p className="text-primary-700 font-sans">正文内容</p>
Notes
- 🚨
index.cssIt is strictly forbidden to write*、body、htmland other global selector styles in, as these will break TailwindCSS's preflight reset and cause all styles to malfunction.index.cssOnly@import "tailwindcss"+@theme - Use TailwindCSS v4(Vite plugin mode), install
tailwindcssand@tailwindcss/vite - v4 not required
postcss、autoprefixer、tailwind.config.js, and also not requirednpx tailwindcss init - The CSS entry uses
@import "tailwindcss"instead of v3's@tailwinddirectives - Theme customization uses CSS
@themedirectives, instead oftailwind.config.js - All icons uniformly use lucide-react; do not mix in other icon libraries
- Mock data is placed in the
src/mock/directory, making it easy to replace with a real API later - Components should preferentially use functional components + TypeScript
- Follow the design specifications in DESIGN.md for style development
verbatimModuleSyntaxmust be set tofalse, to avoid runtime errors from type imports- When using the
cn()utility function, make sureclsxandtailwind-merge - After development is complete, you must use agent-browser to start the page for self-test verification