Environment Setup — Vite + React Project Configuration
Why Choose Vite?
Until 2021, Create React App (CRA) was the official scaffolding tool. However, CRA used Webpack internally, causing the development server to take tens of seconds to start. The current React official documentation recommends Vite, Next.js, Remix, and others instead of CRA.
CRA vs Vite Comparison
| Feature | CRA | Vite |
|---|---|---|
| Bundler | Webpack | esbuild (dev) + Rollup (build) |
| Cold start | 20–60 seconds | 1–3 seconds |
| HMR speed | Slow | Instant |
| Maintenance | Effectively discontinued | Actively developed |
| Bundle size | Larger | Smaller |
Creating a Vite + React Project
Basic Setup
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
TypeScript Template
npm create vite@latest my-react-app -- --template react-ts
Interactive Mode
npm create vite@latest
# Follow the prompts:
# ✔ Project name: my-app
# ✔ Select a framework: React
# ✔ Select a variant: JavaScript / TypeScript / JavaScript + SWC / TypeScript + SWC
SWC (Speedy Web Compiler) is an ultra-fast Babel replacement written in Rust. If performance matters, choose the
react-swcvariant.
Project Folder Structure
my-react-app/
├── public/ # Static files (copied as-is)
│ └── vite.svg
├── src/ # Source code
│ ├── assets/ # Images, fonts, etc.
│ ├── components/ # Reusable components
│ ├── pages/ # Page components (when using a router)
│ ├── hooks/ # Custom hooks
│ ├── App.jsx # Root component
│ ├── App.css
│ ├── main.jsx # Entry point
│ └── index.css
├── index.html # Vite's entry HTML
├── vite.config.js # Vite configuration
├── package.json
└── .eslintrc.cjs # ESLint configuration
main.jsx — Entry Point
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
);
App.jsx — Root Component
import { useState } from 'react';
import reactLogo from './assets/react.svg';
import './App.css';
function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Vite + React</h1>
<button onClick={() => setCount(count + 1)}>
count is {count}
</button>
</div>
);
}
export default App;
Development Commands
npm run dev # Start development server (default port: 5173)
npm run build # Production build (generates dist/ folder)
npm run preview # Preview build output locally
npm run lint # Run ESLint
Changing the Port
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3000, // Development server port
open: true, // Automatically open browser
},
});
Path Alias Configuration
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@hooks': path.resolve(__dirname, './src/hooks'),
},
},
});
You can then use absolute paths for imports.
// Before
import Button from '../../components/Button';
// After
import Button from '@components/Button';
ESLint + Prettier Setup
Installation
npm install -D eslint-config-prettier prettier
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin # For TypeScript
.eslintrc.cjs
module.exports = {
root: true,
env: { browser: true, es2024: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'prettier', // Disable rules that conflict with Prettier
],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
plugins: ['react-refresh'],
rules: {
'react/react-in-jsx-scope': 'off', // React 17+ automatic JSX Transform
'react-refresh/only-export-components': 'warn',
},
};
.prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}
Installing React DevTools
Install as a browser extension.
Key Features
| Tab | Purpose |
|---|---|
| Components | Browse component tree, inspect and edit props/state |
| Profiler | Measure rendering performance, see which components rendered and why |
DevTools Tips
// Set displayName on a component (useful for anonymous functions)
const MemoizedCard = React.memo(function Card({ title }) {
return <div>{title}</div>;
});
MemoizedCard.displayName = 'MemoizedCard';
Practical Example: Recommended Folder Structure (Feature-based)
src/
├── features/
│ ├── auth/
│ │ ├── components/ LoginForm.jsx, RegisterForm.jsx
│ │ ├── hooks/ useAuth.js
│ │ ├── api.js # Auth-related API calls
│ │ └── index.js # Public API export
│ └── products/
│ ├── components/ ProductCard.jsx, ProductList.jsx
│ ├── hooks/ useProducts.js
│ └── index.js
├── shared/
│ ├── components/ Button.jsx, Modal.jsx, Input.jsx
│ ├── hooks/ useDebounce.js, useLocalStorage.js
│ └── utils/ formatDate.js, validators.js
├── App.jsx
└── main.jsx
Pro Tips
1. Vite Environment Variables
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
// Using in a component
const apiUrl = import.meta.env.VITE_API_URL;
Only variables prefixed with VITE_ are exposed to the client.
2. Export Rules for HMR
// ✅ One named export or default export per component
export default function Counter() { /* ... */ }
// ❌ Exporting a component and other values from the same file causes HMR warnings
export const CONSTANT = 42;
export default function Counter() { /* ... */ }
3. Checking Build Output
npm run build
# Check file sizes in the dist/ folder
# index-[hash].js: app code
# vendor-[hash].js: node_modules code (split into chunks)
You can use the vite-bundle-visualizer package to visually analyze the bundle composition.