Understanding the React Project Structure

Understanding what every file in your Vite React project does โ€” and why โ€” gives you the confidence to modify the scaffold, add new files correctly, and debug problems when the build or runtime behaves unexpectedly. In this lesson you will walk through every file in the generated project, understand the relationship between index.html, main.jsx, and App.jsx, and reorganise the folder structure into the architecture the MERN Blog client will use throughout Part 4.

The Generated Files โ€” Explained

File Purpose Do You Edit It?
index.html HTML shell โ€” the single page React mounts into. Contains the <div id="root"> where React renders. Rarely โ€” add <meta> tags, title, fonts
src/main.jsx Entry point โ€” imports React, ReactDOM, and App. Calls ReactDOM.createRoot().render() to mount the app. Rarely โ€” add global providers (Router, Context)
src/App.jsx Root component โ€” the top of your component tree. Usually sets up routing. Yes โ€” add routes, global layout
src/index.css Global CSS โ€” applies to the whole application. Yes โ€” add resets, CSS variables, base styles
src/App.css Styles scoped to App.jsx. Optional โ€” often deleted and replaced with a CSS Modules approach
vite.config.js Vite configuration โ€” plugins, dev server proxy, build options. Yes โ€” add proxy, aliases, plugins
package.json Project manifest โ€” dependencies, scripts. Yes โ€” add packages, update scripts
public/ Static assets served at the root URL. Files here bypass Vite processing. Yes โ€” add favicons, robots.txt, static images
Note: The key difference between public/ and src/assets/: files in public/ are copied to dist/ as-is and served at their exact path (e.g. public/logo.png โ†’ /logo.png). Files in src/assets/ are processed by Vite โ€” they get content-hashed filenames in the build, optimised, and tree-shaken. Import assets from src/assets/ when they are used in components; put truly static files (favicon, robots.txt) in public/.
Tip: Add a jsconfig.json (or tsconfig.json for TypeScript) at the client root with "paths": { "@/*": ["./src/*"] } and configure the same alias in vite.config.js. This gives you absolute imports: import Button from '@/components/ui/Button' instead of fragile relative paths like import Button from '../../../components/ui/Button'.
Warning: Every file in src/ is processed by Vite and included in the bundle only if it is imported somewhere. Files that are not imported will be tree-shaken and excluded from the production build. If you add an image to src/assets/ but never import it in a component, it will not appear in the built site. Move truly standalone static files to public/ instead.

index.html โ€” The HTML Shell

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MERN Blog</title>
  </head>
  <body>
    <!-- React mounts here โ€” this div is the entire SPA container -->
    <div id="root"></div>

    <!-- Vite injects the JS bundle here at build time -->
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

main.jsx โ€” The Entry Point

// src/main.jsx
import { StrictMode } from 'react';
import { createRoot }  from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // added in MERN Blog
import './index.css';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <BrowserRouter>   {/* Router wraps the entire app */}
      <App />
    </BrowserRouter>
  </StrictMode>
);

The MERN Blog Client Structure

client/src/
โ”œโ”€โ”€ assets/               โ† Images, SVGs imported in components
โ”œโ”€โ”€ components/           โ† Reusable UI components (Button, Modal, PostCard...)
โ”‚   โ”œโ”€โ”€ ui/               โ† Generic UI primitives
โ”‚   โ””โ”€โ”€ layout/           โ† Header, Footer, Sidebar, Layout wrapper
โ”œโ”€โ”€ pages/                โ† Page-level components (one per route)
โ”‚   โ”œโ”€โ”€ HomePage.jsx
โ”‚   โ”œโ”€โ”€ PostPage.jsx
โ”‚   โ”œโ”€โ”€ LoginPage.jsx
โ”‚   โ””โ”€โ”€ RegisterPage.jsx
โ”œโ”€โ”€ services/             โ† Axios API call functions (one file per resource)
โ”‚   โ”œโ”€โ”€ authService.js
โ”‚   โ””โ”€โ”€ postService.js
โ”œโ”€โ”€ context/              โ† React Context providers (AuthContext, ThemeContext)
โ”‚   โ””โ”€โ”€ AuthContext.jsx
โ”œโ”€โ”€ hooks/                โ† Custom hooks (usePosts, useAuth, useForm)
โ”‚   โ””โ”€โ”€ useAuth.js
โ”œโ”€โ”€ utils/                โ† Helper functions (formatDate, truncate...)
โ”‚   โ””โ”€โ”€ helpers.js
โ”œโ”€โ”€ App.jsx               โ† Root component โ€” defines routes
โ”œโ”€โ”€ main.jsx              โ† Entry point โ€” mounts React
โ””โ”€โ”€ index.css             โ† Global styles

Configuring Path Aliases in Vite

// client/vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'), // @ โ†’ src/
    },
  },
  server: {
    port: 5173,
    proxy: {
      '/api': { target: 'http://localhost:5000', changeOrigin: true },
    },
  },
});
// client/jsconfig.json โ€” for VS Code IntelliSense with the alias
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] }
  }
}

Common Mistakes

Mistake 1 โ€” Putting all components in one flat directory

โŒ Wrong โ€” all 30+ component files in src/components/ with no organisation:

src/components/
  Header.jsx, Footer.jsx, Button.jsx, Modal.jsx, PostCard.jsx,
  LoginForm.jsx, RegisterForm.jsx, PostForm.jsx...
// Finding a specific component requires scanning the whole list

โœ… Correct โ€” group by domain and responsibility from day one:

src/components/ui/         (Button, Input, Modal, Spinner)
src/components/layout/     (Header, Footer, Sidebar)
src/components/posts/      (PostCard, PostList, PostForm)
src/components/auth/       (LoginForm, RegisterForm)

Mistake 2 โ€” Writing API calls directly in component files

โŒ Wrong โ€” fetch calls scattered across component files:

// PostCard.jsx
useEffect(() => {
  axios.delete(`/api/posts/${id}`, { headers: { Authorization: `Bearer ${token}` } });
}, []);

โœ… Correct โ€” centralise all API calls in service files:

// services/postService.js
export const deletePost = (id) => axios.delete(`/api/posts/${id}`);

// PostCard.jsx
import { deletePost } from '@/services/postService';
const handleDelete = () => deletePost(id);

Mistake 3 โ€” Not wrapping the app in BrowserRouter

โŒ Wrong โ€” using React Router hooks without a Router provider:

Error: useNavigate() may be used only in the context of a <Router> component

โœ… Correct โ€” wrap the entire app in BrowserRouter in main.jsx (already shown above).

Quick Reference

File / Folder Purpose
index.html HTML shell with <div id="root">
src/main.jsx Entry point โ€” mounts React into the DOM
src/App.jsx Root component โ€” routing and global layout
src/pages/ One component per route
src/components/ Reusable UI pieces
src/services/ Axios API call functions
src/context/ React Context providers
src/hooks/ Custom React hooks
public/ Static files (favicon, robots.txt) โ€” not processed by Vite
vite.config.js Vite config โ€” proxy, aliases, plugins

🧠 Test Yourself

You add an image hero.jpg to src/assets/ but it never appears in the production build output in dist/. What is the cause?