Setup

Fastest way to a working React + TypeScript project with Vite. From zero to dev server in under a minute.

Create the Project

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

Dev server runs at http://localhost:5173 with hot module replacement (HMR). Changes appear instantly without full page reloads.

Project Structure

my-app/
├── index.html            # Entry HTML - Vite uses this as the entry point
├── package.json
├── tsconfig.json          # TypeScript config
├── tsconfig.app.json      # App-specific TS config (extends base)
├── vite.config.ts         # Vite config
├── public/                # Static assets (copied as-is to build output)
│   └── vite.svg
└── src/
    ├── main.tsx           # Entry point - renders App into #root
    ├── App.tsx            # Root component
    ├── App.css            # Component styles
    ├── index.css          # Global styles
    └── vite-env.d.ts      # Vite type declarations

Key files to understand:

// src/main.tsx - the entry point
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Tip: StrictMode runs your components twice in development to catch bugs (impure renders, missing cleanup). It does nothing in production. Leave it on.

// src/App.tsx - your root component
function App() {
  return (
    <main>
      <h1>My App</h1>
    </main>
  );
}

export default App;

Add Tailwind CSS

npm install -D tailwindcss @tailwindcss/vite

Add the plugin to Vite config:

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

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

Replace src/index.css with:

@import "tailwindcss";

Test it works:

// src/App.tsx
function App() {
  return (
    <main className="min-h-screen bg-zinc-950 text-zinc-100 flex items-center justify-center">
      <h1 className="text-4xl font-bold">It works</h1>
    </main>
  );
}

export default App;

Add Path Aliases

Avoid ../../.. import chains. Configure @/ to point to src/:

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

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});
// tsconfig.app.json - add to compilerOptions
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Now you can import anywhere:

import { Button } from "@/components/Button";
import { useAuth } from "@/hooks/useAuth";
src/
├── components/            # Shared/reusable components
│   ├── ui/                # Primitives (Button, Input, Card)
│   └── layout/            # Layout components (Header, Sidebar)
├── hooks/                 # Custom hooks
├── lib/                   # Utility functions, API clients
├── types/                 # TypeScript type definitions
├── pages/                 # Page-level components (if using a router)
├── App.tsx
├── main.tsx
└── index.css

Add a Router (Optional)

For multi-page apps, add React Router:

npm install react-router
// src/App.tsx
import { BrowserRouter, Routes, Route } from "react-router";
import { Home } from "@/pages/Home";
import { About } from "@/pages/About";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Build for Production

npm run build    # Output in dist/
npm run preview  # Preview production build locally

Vite produces optimized, code-split bundles. The output is static HTML/CSS/JS that can be deployed to any static host (Vercel, Netlify, Cloudflare Pages, S3).

ESLint Setup

Vite’s template includes a basic ESLint config. Upgrade it for better React support:

npm install -D eslint-plugin-react-hooks eslint-plugin-react-refresh

The React hooks plugin catches:

  • Hooks called inside conditions or loops
  • Missing dependencies in useEffect
  • Non-hook functions named with use prefix

Tip: If you enable React Compiler later, its ESLint plugin replaces the hooks plugin for dependency validation. See React Compiler.

Dev Server Features

Vite’s dev server gives you:

  • HMR - component changes reflect instantly, state is preserved
  • Fast startup - no bundling on start, uses native ES modules
  • TypeScript - type-checked by your editor, transpiled (not checked) by Vite for speed
  • Error overlay - compile errors show in the browser

Gotcha: Vite transpiles TypeScript but doesn’t type-check it. Type errors won’t stop the dev server. Run tsc --noEmit separately (or rely on your editor) for type checking.

Next: First App to build something real