Vite Project Setup — Fast Tooling for Modern React

Vite is the modern build tool for React applications, replacing Create React App in most new projects. Where CRA bundles all your code on startup (slow for large projects), Vite serves files directly as native ES modules during development — the browser loads only what it needs, when it needs it. The result is a development server that starts in under a second regardless of project size, with hot module replacement (HMR) that applies code changes in milliseconds without a full page reload. For production, Vite uses Rollup to bundle everything efficiently.

Creating a React + Vite Project

# Create a new React project with Vite
npm create vite@latest blog-frontend -- --template react
# Or with TypeScript (recommended for larger projects):
npm create vite@latest blog-frontend -- --template react-ts

cd blog-frontend
npm install
npm run dev   # starts dev server at http://localhost:5173

# Project structure:
# blog-frontend/
# ├── index.html          ← entry point (Vite serves this)
# ├── src/
# │   ├── main.jsx        ← mounts React app into index.html
# │   ├── App.jsx         ← root component
# │   ├── App.css
# │   └── assets/
# ├── public/             ← static files (copied as-is to dist/)
# ├── vite.config.js      ← Vite configuration
# └── package.json
Note: Vite uses .jsx (or .tsx) for files containing JSX and .js (or .ts) for plain JavaScript. You must use the correct extension — Vite will not process JSX syntax in a .js file by default. If you see “Unexpected token” errors when using JSX, the first thing to check is whether the file has the .jsx extension.
Tip: Configure a proxy in vite.config.js to forward API requests to your FastAPI backend during development. With proxy: {"/api": "http://localhost:8000"}, a browser request to /api/posts is forwarded to http://localhost:8000/api/posts. This avoids CORS issues during development and means you do not need to hardcode the backend URL in your frontend code — the proxy is only active in development; production uses a real reverse proxy (Nginx).
Warning: The public/ directory is for static assets that should be served at the root URL without processing (favicons, robots.txt, manifest files). Files in src/assets/ are imported directly in JavaScript and processed by Vite (hashed filenames, optimised). Use public/ for files that need a stable URL; use src/assets/ for images and fonts referenced in components.

Vite Configuration for the Blog Application

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
    plugins: [react()],

    // Path alias — @/ maps to src/ for cleaner imports
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "src"),
        },
    },

    // Dev server configuration
    server: {
        port: 5173,

        // Proxy API requests to FastAPI backend
        proxy: {
            "/api": {
                target:      "http://localhost:8000",
                changeOrigin: true,
                // rewrite: (path) => path.replace(/^\/api/, ""),
                // Uncomment if FastAPI routes don't have /api prefix
            },
            "/uploads": {
                target:      "http://localhost:8000",
                changeOrigin: true,
            },
        },
    },

    // Build configuration
    build: {
        outDir:         "dist",
        sourcemap:      true,   // source maps for debugging
        rollupOptions: {
            output: {
                manualChunks: {
                    // Split vendor code for better caching
                    vendor: ["react", "react-dom"],
                },
            },
        },
    },
});

The Entry Point and Root Component

// index.html — Vite's entry point
// <!DOCTYPE html>
// <html lang="en">
// <body>
//   <div id="root"></div>  ← React mounts here
//   <script type="module" src="/src/main.jsx"></script>
// </body>
// </html>

// src/main.jsx — mounts the React app
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>
);

// StrictMode: enables extra development warnings and double-invokes
// lifecycle methods to detect side effects — remove for production profiling

// src/App.jsx — root component
export default function App() {
    return (
        <div className="app">
            <h1>Blog Application</h1>
        </div>
    );
}

Common Mistakes

Mistake 1 — Using .js extension for files containing JSX

❌ Wrong:

src/components/PostCard.js   # contains JSX — Vite gives syntax error!

✅ Correct:

src/components/PostCard.jsx  # ✓ .jsx extension for files with JSX

Mistake 2 — Importing with wrong path after setting alias

❌ Wrong — relative path from deep in the tree:

import PostCard from "../../../components/PostCard"   // fragile path!

✅ Correct — use the @/ alias:

import PostCard from "@/components/PostCard"   // ✓ always from src root

Mistake 3 — Running npm run build instead of npm run dev during development

❌ Wrong — build creates a production bundle (slow, no HMR).

✅ Correct — npm run dev for development (fast HMR), npm run build only for production deployment.

Quick Reference

Task Command / File
Create project npm create vite@latest name -- --template react
Start dev server npm run dev → http://localhost:5173
Production build npm run build → dist/
Preview production npm run preview
Config file vite.config.js
Path alias resolve.alias: {"@": path.resolve(__dirname, "src")}
API proxy server.proxy: {"/api": "http://localhost:8000"}

🧠 Test Yourself

Your React dev server is running on port 5173 and FastAPI on port 8000. You make a fetch request to /api/posts from React. Without a Vite proxy, what happens?