Vite for React SPA

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5168

    #1

    Vite for React SPA

    πŸ”₯ Why Vite is Great for SPA React Projects

    1. Blazing Fast Dev Experience

    • ESM-based Hot Module Replacement (HMR): Vite uses native ES Modules, so dev server startup is instant, and code changes reflect nearly instantly.
    • Unlike older tools like Create React App (CRA), Vite doesn't bundle everything upfrontβ€”only what’s needed.


    2. Minimal Config, Extensible When Needed

    • Out of the box: βœ… JSX support, βœ… TypeScript, βœ… CSS Modules/PostCSS, βœ… Environment variables.
    • Need more? It uses Rollup under the hood for production, so you can customize or add plugins easily.


    3. First-Class TypeScript + React Support





    npm create vite@latest my-app --template react-ts







    It scaffolds a clean project in seconds. You’re ready to go with full TypeScript and React setup.


    4. Lightweight & SPA-Ready

    • Vite is perfect for SPAs because:
      • It supports React Router easily.
      • You don’t get the overhead of a backend framework like Next.js or Remix.
      • You can control routing and deployment exactly how you want (e.g., with static hosting on Vercel, Netlify, etc.).


    5. Built-in Features

    • Code splitting
    • Tree shaking
    • Built-in dev server
    • Easy static asset handling





    βš–οΈ When to Choose Vite Over Next.js or CRA

    Pure SPA (no SSR or API layer) βœ… Best Choice
    Want fast startup & dev speed βœ… Ideal
    Full control over routing/build βœ… Preferable
    Don’t need server-side rendering βœ… Good fit
    Need backend/API integration ❌ Consider NestJS + Vite or Next.js


    If you want a frontend-only app that fetches from APIs (like a NestJS backend), Vite is excellent.





    πŸ’‘ Example: Create Vite + React + TypeScript Project





    npm create vite@latest my-app --template react-ts
    cd my-app
    npm install
    npm run dev







    πŸ“ Folder Structure Preview





    my-app/
    β”œβ”€ src/
    β”‚ β”œβ”€ App.tsx
    β”‚ β”œβ”€ main.tsx
    β”‚ └─ assets/
    β”œβ”€ public/
    β”œβ”€ index.html
    β”œβ”€ vite.config.ts










    🧠 Best Practices with Vite in SPA

    • Use React Router DOM for client-side routing.
    • Use .env files for API keys, base URLs.
    • Keep Vite’s config minimal unless you need advanced Rollup behavior.
    • For production: run vite build and deploy the dist folder to static hosting.





    ⚠️ When Vite isn't Ideal

    If you need:
    • SSR (Server-side rendering) β†’ Use Next.js
    • Incremental static regeneration (ISR) or hybrid SSR β†’ Use Next.js
    • Backend + frontend in one β†’ Use NestJS + Vite with Vite as frontend





    βœ… Verdict

    Yes, Vite is arguably the best option today for building a modern, fast, and clean React SPA if:
    • You don’t need SSR.
    • You want fast builds and DX.
    • You’re building a frontend-only client (e.g., talking to a NestJS API).





    vite.config.ts for a React SPA using Vite that proxies API requests to a NestJS backend (which might run at http://localhost:3000).





    πŸ›  Vite Proxy Config for NestJS Backend

    βœ… Goals:






    πŸ“„ vite.config.ts





    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'

    export default defineConfig({
    plugins: [react()],
    server: {
    port: 5173, // Frontend port
    proxy: {
    '/api': {
    target: 'http://localhost:3000', // NestJS backend
    changeOrigin: true,
    rewrite: path => path.replace(/^\/api/, ''),
    },
    },
    },
    })










    πŸ“ Frontend Call Example (e.g., using Axios or Fetch)





    // This call from React (port 5173) will be proxied to NestJS (port 3000)
    fetch('/api/users')
    .then(res => res.json())
    .then(data => console.log(data))







    Behind the scenes:

    /api/users β†’ proxied to β†’ http://localhost:3000/users



    πŸ’‘ Bonus: Handling Environment Variables

    Add a .env file at the root of your Vite project:






    VITE_API_BASE_URL=/api







    Then in your code:






    const apiUrl = import.meta.env.VITE_API_BASE_URL;
    fetch(`${apiUrl}/users`);










    πŸ”’ Don't forget for Production

    In production:
    • You need to handle the /api routing via reverse proxy (e.g., Nginx, Vercel rewrites, or within your backend).
    • Or configure your frontend to use the full backend URL directly if they're deployed separately.





    The full NestJS + Vite Docker dev setup as well:




    complete Docker-based development setup for a fullstack Vite (React) + NestJS app. This includes:
    • βœ… Yarn workspaces or separate folders
    • βœ… Live reload in both apps
    • βœ… Vite proxy to NestJS for /api
    • βœ… Docker + docker-compose dev setup





    πŸ—οΈ Folder Structure (Monorepo or Parallel)

    You can go with one of the following:


    Option A – Monorepo-style:





    fullstack-app/
    β”œβ”€β”€ client/ # Vite + React
    β”œβ”€β”€ server/ # NestJS
    β”œβ”€β”€ docker-compose.yml
    └── README.md










    1️⃣ Vite (React) Frontend Setup

    client/vite.config.ts






    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'

    export default defineConfig({
    plugins: [react()],
    server: {
    host: true, // Allow Docker access
    port: 5173,
    proxy: {
    '/api': {
    target: 'http://server:3000', // use Docker service name
    changeOrigin: true,
    rewrite: path => path.replace(/^\/api/, ''),
    },
    },
    },
    })







    client/package.json






    {
    "name": "client",
    "version": "0.0.1",
    "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
    },
    "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
    },
    "devDependencies": {
    "@vitejs/plugin-react": "^4.0.0",
    "vite": "^5.0.0"
    }
    }










    2️⃣ NestJS Backend Setup

    server/main.ts






    import { NestFactory } from '@nestjs/core'
    import { AppModule } from './app.module'

    async function bootstrap() {
    const app = await NestFactory.create(AppModule)
    app.setGlobalPrefix('api') // Base path for APIs
    await app.listen(3000, '0.0.0.0')
    }
    bootstrap()







    server/package.json






    {
    "name": "server",
    "version": "1.0.0",
    "scripts": {
    "start:dev": "nest start --watch"
    },
    "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^7.8.1"
    },
    "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/schematics": "^10.0.0",
    "ts-node": "^10.9.1",
    "typescript": "^5.0.0"
    }
    }










    3️⃣ Docker Setup

    client/Dockerfile





    # --- React Dev ---
    FROM node:18-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install
    COPY . .
    EXPOSE 5173
    CMD ["yarn", "dev"]







    server/Dockerfile





    # --- NestJS Dev ---
    FROM node:18-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install
    COPY . .
    EXPOSE 3000
    CMD ["yarn", "start:dev"]







    Root docker-compose.yml





    version: '3.8'

    services:
    client:
    build:
    context: ./client
    ports:
    - "5173:5173"
    volumes:
    - ./client:/app
    - /app/node_modules
    depends_on:
    - server

    server:
    build:
    context: ./server
    ports:
    - "3000:3000"
    volumes:
    - ./server:/app
    - /app/node_modules
    command: yarn start:dev










    βœ… Usage

    πŸ”§ 1. Start the Dev Stack





    docker-compose up --build

    βš™οΈ 2. Make a test route in NestJS

    server/src/app.controller.ts






    @Get('hello')
    getHello(): string {
    return 'Hello from NestJS';
    }







    Then test in your React app:






    useEffect(() => {
    fetch('/api/hello')
    .then(res => res.text())
    .then(console.log)
    }, []);










    πŸ“¦ Optional: Add MongoDB to Compose

    Want to persist data? Add this to docker-compose.yml:






    mongo:
    image: mongo:6
    ports:
    - "27017:27017"
    volumes:
    - mongo-data:/data/db

    volumes:
    mongo-data:







    Then in NestJS, connect to mongodb://mongo:27017.





    🧠 Recap

    Frontend Vite Fast React SPA
    Backend NestJS Modular API w/ TypeScript
    Proxy Vite Dev /api β†’ NestJS
    Runtime Docker Isolated Dev Envs




    More...
Working...