Project Structure — Organising a Real React Application

A well-structured React project is as important as well-structured backend code. Files scattered in a flat directory become unmanageable as the application grows. The standard approach organises by feature or type: a components/ folder for reusable UI pieces, a pages/ folder for route-level components, hooks/ for custom hooks, services/ for API calls, and stores/ for state management. With path aliases, absolute imports replace fragile relative paths. With ESLint and Prettier, code quality is enforced automatically. This lesson sets up the complete development environment for the blog application’s frontend.

blog-frontend/
├── public/
│   └── favicon.svg
│
├── src/
│   ├── main.jsx                   ← entry point
│   ├── App.jsx                    ← root component + router setup
│   │
│   ├── components/                ← reusable UI components
│   │   ├── ui/                    ← primitive: Button, Input, Badge, Modal
│   │   │   ├── Button.jsx
│   │   │   ├── Badge.jsx
│   │   │   └── Input.jsx
│   │   ├── layout/                ← layout: Header, Sidebar, Footer
│   │   │   ├── Header.jsx
│   │   │   └── Layout.jsx
│   │   └── post/                  ← feature: PostCard, PostForm, CommentList
│   │       ├── PostCard.jsx
│   │       ├── PostForm.jsx
│   │       └── CommentList.jsx
│   │
│   ├── pages/                     ← route-level components
│   │   ├── HomePage.jsx
│   │   ├── PostDetailPage.jsx
│   │   ├── LoginPage.jsx
│   │   └── DashboardPage.jsx
│   │
│   ├── hooks/                     ← custom React hooks
│   │   ├── useAuth.js
│   │   ├── usePosts.js
│   │   └── useInfiniteScroll.js
│   │
│   ├── services/                  ← API communication
│   │   ├── api.js                 ← base axios/fetch instance with auth
│   │   ├── posts.js               ← post CRUD API calls
│   │   └── auth.js                ← login, register, refresh
│   │
│   ├── stores/                    ← global state (Zustand or Context)
│   │   └── authStore.js
│   │
│   └── utils/                     ← pure utility functions
│       ├── formatDate.js
│       └── slugify.js
│
├── vite.config.js
├── .eslintrc.cjs
├── tailwind.config.js
└── package.json
Note: There is no single “correct” React project structure — different teams use different conventions. The structure above follows a hybrid approach: grouping by type at the top level (components, pages, hooks) with feature sub-folders inside (components/post/, components/ui/). An alternative is feature-first organisation where everything for “posts” lives in src/features/posts/ including its components, hooks, and API calls. Either works; consistency within the project matters more than which convention you choose.
Tip: Install and configure React DevTools — a browser extension (Chrome/Firefox) that adds a React panel to devtools. You can inspect the component tree, examine props and state at runtime, and profile rendering performance. It is the single most useful debugging tool for React development. Install from the Chrome Web Store (search “React Developer Tools”) and enable it — a React icon appears in the browser toolbar when you are on a React page.
Warning: Tailwind CSS’s utility classes are generated only for classes that appear in your source files. If you dynamically construct class names (like `text-${colour}-600`), Tailwind’s JIT compiler cannot detect them and the classes will be missing in production. Always use complete class names in JSX: colour === "blue" ? "text-blue-600" : "text-red-600". Never dynamically construct partial Tailwind class names.

Installing Tailwind CSS

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p   # creates tailwind.config.js and postcss.config.js
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
    content: [
        "./index.html",
        "./src/**/*.{js,ts,jsx,tsx}",   // scan these files for class names
    ],
    theme: {
        extend: {
            colors: {
                brand: {
                    50:  "#eff6ff",
                    500: "#3b82f6",
                    900: "#1e3a8a",
                },
            },
        },
    },
    plugins: [],
};
/* src/index.css — Tailwind directives */
@tailwind base;
@tailwind components;
@tailwind utilities;

ESLint and Prettier Setup

npm install -D eslint eslint-plugin-react eslint-plugin-react-hooks prettier
// .eslintrc.cjs
module.exports = {
    env: { browser: true, es2020: true },
    extends: [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:react-hooks/recommended",
    ],
    parserOptions: { ecmaVersion: "latest", sourceType: "module" },
    settings: { react: { version: "detect" } },
    rules: {
        "react/react-in-jsx-scope": "off",  // not needed with Vite
        "react/prop-types": "warn",          // warn on missing prop types
        "no-unused-vars": "warn",
    },
};

// .prettierrc
// { "semi": true, "singleQuote": false, "tabWidth": 4, "trailingComma": "es5" }

Common Mistakes

Mistake 1 — Dynamic Tailwind class construction

❌ Wrong — class not included in production build:

// Tailwind can't see "text-blue-600" or "text-red-600" — only "text-${variant}-600"
<span className={`text-${variant}-600`}>...</span>

✅ Correct — use complete class names:

const colours = { blue: "text-blue-600", red: "text-red-600" };
<span className={colours[variant]}>...</span>   // ✓ full class names

Mistake 2 — Deeply nested relative imports

❌ Wrong — fragile paths break on file moves:

import Button from "../../../components/ui/Button"   // what if the file moves?

✅ Correct — use @ alias configured in vite.config.js:

import Button from "@/components/ui/Button"   // ✓ always relative to src/

Quick Reference — Dev Workflow

Task Command
Start dev server npm run dev
Lint code npm run lint
Format code npx prettier --write src/
Build for production npm run build
Preview production build npm run preview
Install React DevTools Chrome Web Store → “React Developer Tools”

🧠 Test Yourself

You want to style a button with a different Tailwind colour based on a variant prop. You write className={`bg-${variant}-600`}. The button works in development but has no background colour in production. Why?