Skip to main content
Back to Blog

How to Set Up a React.js Project with Vite and TypeScript in 2026

May 5, 2026
Safal Bhattarai
9 min read
ReactReact.jsViteTypeScriptTailwind CSSFrontendTutorial

If you don't need server-side rendering, React with Vite is the fastest, leanest way to build a modern single-page app in 2026. It's what I reach for whenever I want a pure client-side project — admin panels, dashboards, interactive learning tools like LearnWeb, and most of the React projects I teach at MindCraft Academy.

This guide walks through the exact React + Vite setup I use in production: React 19, Vite 6, TypeScript, Tailwind CSS v4, React Router 7, ESLint, and a folder structure that scales.

React + Vite vs. Next.js — which should you pick?

Quick decision tree:

  • Pick Next.js if you need SEO (marketing site, blog, e-commerce), server-side rendering, image optimization, or full-stack features (API routes, Server Actions). Read my Next.js 15 setup guide instead.
  • Pick React + Vite if you're building an SPA behind authentication — dashboards, admin panels, internal tools, prototypes, learning projects. SEO doesn't matter and you want the lightest possible setup.

Prerequisites

  • Node.js 20 LTS or 22 — check with node -v.
  • A package managerpnpm, npm, or yarn.
  • VS Code with the ES7+ React/Redux snippets, ESLint, and Tailwind CSS IntelliSense extensions.

Step 1 — Scaffold with Vite

Vite ships a curated React + TypeScript template. Run:

pnpm create vite@latest my-app --template react-ts
cd my-app
pnpm install

Vite picks React 19 and TypeScript 5+. The whole setup takes about 30 seconds. Compared to the old create-react-app (which is officially deprecated), Vite cold starts in under 200ms — it's a different planet.

Step 2 — Folder structure

The default Vite layout is fine for tiny projects, but you'll outgrow it fast. Here's what I use:

src/
├── app/                # App-level wiring (router, providers)
│   └── router.tsx
├── components/         # Shared, dumb, reusable UI
│   └── ui/             # Buttons, inputs, cards (atoms)
├── features/           # Feature-scoped code
│   ├── auth/
│   │   ├── pages/
│   │   ├── hooks/
│   │   └── api.ts
│   └── dashboard/
├── lib/                # Pure utilities (formatters, fetchers)
├── hooks/              # Cross-feature hooks
├── styles/
│   └── globals.css
├── types/              # Shared TypeScript types
└── main.tsx            # App entry

The rule I drill into my students: code that's only used inside a feature lives inside that feature folder. Move it to components/ or lib/ only when it's reused by two or more features. Premature sharing is the #1 cause of unmaintainable React apps.

Step 3 — Path aliases

Relative imports get ugly fast (../../../lib/x). Set up an alias once and never look back.

Open vite.config.ts:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "node:path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

Then update tsconfig.json with the matching path:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

Now import Button from "@/components/ui/button" works everywhere.

Step 4 — Tailwind CSS v4

Install the Vite plugin (Tailwind v4 ships its own Vite plugin — no PostCSS dance):

pnpm add -D tailwindcss @tailwindcss/vite

Add it to vite.config.ts:

import tailwindcss from "@tailwindcss/vite";

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

Create src/styles/globals.css with a single line:

@import "tailwindcss";

Import it in main.tsx. Done. No tailwind.config.js, no PostCSS config — Tailwind v4 reads your CSS directly.

Step 5 — React Router 7

For multi-page SPAs:

pnpm add react-router

Wire it in src/app/router.tsx:

import { createBrowserRouter, RouterProvider } from "react-router";
import Home from "@/features/home/pages/Home";
import Dashboard from "@/features/dashboard/pages/Dashboard";

const router = createBrowserRouter([
  { path: "/", element: <Home /> },
  { path: "/dashboard", element: <Dashboard /> },
]);

export function AppRouter() {
  return <RouterProvider router={router} />;
}

React Router 7 unified the old react-router-dom and Remix data APIs — loaders, actions, and nested layouts all work the same way.

Step 6 — State management (pick the smallest tool that works)

The order I reach for things, in 2026:

  1. useState — for component-local state. 90% of cases.
  2. useContext + useReducer — for state shared by 2-3 components in the same subtree.
  3. Zustand — when state crosses feature boundaries. ~1KB, zero boilerplate.
  4. TanStack Query — for server state (anything you fetch). Stop manually caching with useState.
  5. Redux Toolkit — only if your team already knows it and you have very complex client state. Otherwise skip it.

The biggest mistake new React developers make is reaching for Redux on day one. You don't need it. You probably never need it.

Step 7 — ESLint + Prettier

Vite ships ESLint. Add Prettier and the Tailwind plugin:

pnpm add -D prettier prettier-plugin-tailwindcss eslint-config-prettier

Create .prettierrc:

{
  "semi": true,
  "singleQuote": false,
  "trailingComma": "all",
  "plugins": ["prettier-plugin-tailwindcss"]
}

Step 8 — Build and deploy

Vite produces a fully static bundle:

pnpm build
# outputs to dist/

You can drop the dist/ folder on any static host — Vercel, Netlify, Cloudflare Pages, GitHub Pages, or your own NGINX server. For free hosting, Vercel and Netlify both auto-detect Vite. Connect your GitHub repo and you're live in under a minute.

Frequently asked questions

Should I use create-react-app in 2026?

No — CRA is officially deprecated. Use Vite or Next.js.

Vite vs Webpack — does it matter?

Hugely for developer experience. Vite cold-starts in milliseconds and hot-reloads instantly because it uses native ES modules in dev. You'll never go back.

How do I add server-side logic to a Vite React app?

You don't — a Vite SPA is client-only. Pair it with a separate Node.js API (see my guide on building a REST API with Node.js, Express, and MongoDB) or switch to Next.js if you want them in one project.

What about Remix?

Remix is now part of React Router 7 — the patterns merged. If you wanted Remix-style data loading, you can get it inside your Vite + React Router project.

Conclusion

That's the setup I use for every internal tool, admin panel, and student project. It's deliberately small — five dependencies do 90% of the work. Add what you need when you need it, not before.

If you want to see this exact stack running, check out LearnWeb, my interactive React learning platform. And if you want help shipping a React project, drop me a message.

Safal Bhattarai

Safal Bhattarai is a Full Stack Developer · Tech Lead · Product Builder based in Pokhara, Nepal, shipping production web apps with Next.js, Node.js, MongoDB, PostgreSQL, and Redis.