Lessons in this course (17)

5. React project folder structure — with sample files (Vite + TypeScript)

Basic★ Must knowLesson 5 of 17

npm create vite@latest shop-ui -- --template react-ts creates a client-side React app. A real project grows into a feature-based structure:

text
shop-ui/
├── index.html                    the single HTML page; <div id="root"> where React mounts
├── public/                       static files copied as-is (favicon, robots.txt)
├── src/
│   ├── main.tsx                  entry point: createRoot(...).render(<App />) with providers (Router, QueryClient)
│   ├── App.tsx                   root component: layout + routes
│   ├── routes.tsx                route table (React Router)
│   ├── index.css                 global styles
│   ├── assets/                   images/fonts imported by components
│   ├── components/               shared, reusable UI
│   │   ├── ui/Button.tsx, Modal.tsx, Spinner.tsx
│   │   └── layout/Header.tsx, Footer.tsx
│   ├── features/                 one folder per business feature
│   │   ├── products/
│   │   │   ├── api.ts            calls to the backend API (fetch/axios)
│   │   │   ├── types.ts          Product, PagedResult<T>
│   │   │   ├── useProducts.ts    custom hook (TanStack Query)
│   │   │   ├── ProductList.tsx   page/feature component
│   │   │   ├── ProductCard.tsx
│   │   │   └── ProductList.test.tsx
│   │   └── orders/ …
│   ├── hooks/                    shared custom hooks (useDebounce, useAuth)
│   ├── lib/                      apiClient.ts (base URL, auth header), utils
│   ├── store/                    global client state (Zustand / Redux Toolkit) if needed
│   └── types/                    shared types
├── .env.development / .env.production    VITE_API_URL=...
├── vite.config.ts                dev server, proxy to the API, build settings
├── tsconfig.json · eslint.config.js · package.json
tsx
// src/main.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import App from "./App";
import "./index.css";

const queryClient = new QueryClient();
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <BrowserRouter><App /></BrowserRouter>
    </QueryClientProvider>
  </StrictMode>
);

// src/App.tsx
import { Routes, Route } from "react-router-dom";
import Header from "./components/layout/Header";
import ProductList from "./features/products/ProductList";
export default function App() {
  return (
    <>
      <Header />
      <Routes>
        <Route path="/" element={<ProductList />} />
        <Route path="*" element={<p>Not found</p>} />
      </Routes>
    </>
  );
}

Next.js projects use the app/ folder conventions instead (see the Next.js course).