How to Set Up a Modern React Project in 2026
·PkgPulse Team
TL;DR
The 2026 React stack: Vite + TypeScript + Biome + Vitest + TanStack Query + Zustand + shadcn/ui. Create React App is deprecated. This guide sets up a production-ready project from scratch — typed, linted, tested, and styled — using the tools developers actually choose in 2026.
Key Takeaways
- Vite: dev server + build (not CRA, not webpack)
- Biome: linting + formatting (not ESLint + Prettier)
- Vitest: unit testing (not Jest)
- TanStack Query: server state (not Redux for API data)
- Zustand: client state (not Redux)
- shadcn/ui: component library (copy-paste, not npm package)
Step 1: Scaffold with Vite
# Official Vite React TypeScript template
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
# Start dev server
npm run dev
# → http://localhost:5173 in ~200ms
Step 2: Add Core Dependencies
# Routing
npm install react-router-dom
# Data fetching + server state
npm install @tanstack/react-query @tanstack/react-query-devtools
# Client state
npm install zustand
# HTTP client
npm install ky
# Form handling + validation
npm install react-hook-form @hookform/resolvers zod
# Date utilities
npm install date-fns
# Class name utilities
npm install clsx tailwind-merge
Step 3: Tailwind CSS + shadcn/ui
# Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# Initialize shadcn/ui
npx shadcn@latest init
# Prompts: style (Default/New York), base color, CSS variables
# Add components as needed
npx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add form
npx shadcn@latest add dialog
Step 4: Biome (Linting + Formatting)
npm install -D --save-exact @biomejs/biome
npx @biomejs/biome init
// biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error",
"useExhaustiveDependencies": "warn"
},
"suspicious": { "noConsoleLog": "warn" }
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5"
}
},
"files": { "ignore": ["dist/**", "node_modules/**"] }
}
// package.json scripts
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"check": "biome check --apply .",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
}
}
Step 5: Vitest + Testing Library
npm install -D vitest @vitest/ui jsdom
npm install -D @testing-library/react @testing-library/user-event @testing-library/jest-dom
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
},
});
// src/test/setup.ts
import '@testing-library/jest-dom';
Step 6: TanStack Query Setup
// src/main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
retry: 1,
},
},
});
createRoot(document.getElementById('root')!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StrictMode>
);
Step 7: Project Structure
src/
├── components/
│ ├── ui/ # shadcn/ui components (auto-generated)
│ └── [feature]/ # Feature-specific components
├── pages/ # Route-level components
├── hooks/ # Custom React hooks
├── lib/
│ ├── api.ts # ky instance + API helpers
│ ├── queryClient.ts
│ └── utils.ts # cn() and other utilities
├── stores/ # Zustand stores
├── test/
│ └── setup.ts
├── types/ # TypeScript type definitions
├── App.tsx
└── main.tsx
Step 8: Environment Variables
# .env.local
VITE_API_URL=http://localhost:3001
VITE_APP_NAME="My App"
// src/vite-env.d.ts — type your env vars
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Final Checklist
✅ Vite + React + TypeScript — scaffolded
✅ Tailwind CSS + shadcn/ui — styled
✅ Biome — linting and formatting configured
✅ Vitest + Testing Library — test infrastructure ready
✅ TanStack Query — server state management
✅ Zustand — client state management
✅ React Router — routing
✅ React Hook Form + Zod — forms and validation
✅ Environment variables typed
✅ Path aliases (tsconfigPaths)
Compare React setup tools on PkgPulse.
See the live comparison
View vite vs. webpack on PkgPulse →