LearnContact
Lesson 819 min read

Understanding Project Structure

Tour a typical Vite React project and learn what each file and folder — from main.jsx to node_modules — is responsible for.

Introduction

When Vite scaffolded your project in the previous lesson, it generated a whole folder of files without much explanation. Before writing more React code, it is worth understanding exactly what each of those files and folders does — so nothing you touch later feels like a mystery.

This lesson tours a typical Vite React project from top to bottom: the src/ and public/ folders, index.html, the two most important files (main.jsx and App.jsx), where new components belong as a project grows, and what node_modules and package-lock.json actually are.

What You Will Learn
  • The overall folder structure Vite generates for a React project.
  • The role of index.html as the single real web page.
  • How main.jsx mounts your app into the DOM.
  • Why App.jsx is called the root component.
  • Where to put new components as your project grows.
  • What node_modules and package-lock.json are for.

Touring the Project

Open the my-app folder from the previous lesson in your editor. At the top level, you will see a mix of configuration files and two important folders, public/ and src/.

Project Structure
my-app/
├── node_modules/
├── public/
│   └── vite.svg
├── src/
│   ├── assets/
│   │   └── react.svg
│   ├── App.css
│   ├── App.jsx
│   ├── index.css
│   └── main.jsx
├── .gitignore
├── index.html
├── package.json
├── package-lock.json
└── vite.config.js

public/

Static files copied as-is into the final build — favicons, robots.txt, and images referenced by absolute path.

src/

Your actual source code lives here — every component, style sheet, and asset you write goes inside this folder.

package.json

Lists your project's dependencies and defines scripts like dev, build, and preview.

package-lock.json

Records the exact version of every installed package so installs are reproducible.

vite.config.js

Configures Vite itself — plugins, the dev server, and build options.

node_modules/

Where every downloaded dependency actually lives on disk after npm install.

index.html — The Real Web Page

Unlike traditional multi-page websites, a React app is a "single-page application": there is exactly one real HTML file, and React renders everything else into it dynamically. That one file is index.html, at the project root.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

Notice two things: the empty <div id="root"></div>, which is the container your entire React application will be injected into, and the <script> tag pointing to src/main.jsx — the JavaScript file that actually starts React.

main.jsx — The Entry Point

src/main.jsx is the very first piece of your own code that runs. Its job is simple but essential: find the #root element from index.html and mount your App component into it.

src/main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import './index.css';

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

createRoot() connects React to the real DOM node with id "root", and .render() tells React what to display inside it — in this case, the <App /> component, wrapped in <StrictMode> (a development-only helper that surfaces potential problems early). You will rarely need to edit main.jsx once it is set up.

App.jsx — The Root Component

src/App.jsx defines the App component — the top of your component tree, sometimes called the "root component." Every other component you write will eventually be rendered somewhere underneath App, directly or indirectly.

src/App.jsx
import './App.css';

function App() {
  return (
    <div className="app">
      <h1>My First React App</h1>
    </div>
  );
}

export default App;

Right now App.jsx is simple, but as your project grows it typically becomes a layout that imports and arranges other, smaller components — a header, a sidebar, a main content area — rather than containing all the markup itself.

Organizing Components as You Grow

A brand-new project only has App.jsx, but real projects quickly grow to dozens or hundreds of components. The common convention is to create a src/components folder, with one file per component.

A Growing Project
src/
├── components/
│   ├── Header.jsx
│   ├── Footer.jsx
│   ├── Button.jsx
│   └── Card.jsx
├── App.jsx
└── main.jsx

This is just a convention, not a rule enforced by React or Vite — but it is the most common pattern you will see in real codebases, and following it makes your project easier for other developers (and future you) to navigate.

node_modules and package-lock.json

When you ran npm install, every package your project depends on — React, ReactDOM, Vite, and their own dependencies — was downloaded into the node_modules folder. It can contain thousands of files, which is why it is never committed to version control (notice it is listed in .gitignore).

package-lock.json works alongside package.json to record the exact version of every single installed package, including nested dependencies. This guarantees that anyone who runs npm install on your project — a teammate, a deployment server, or you on another machine — gets the identical set of package versions.

Why This Matters

package.json lists rough version ranges (like ^5.2.0), which could resolve to slightly different versions over time. package-lock.json pins exact versions, so "works on my machine" problems caused by dependency drift become far less common.

Common Mistakes

Avoid These Mistakes
  • Manually editing files inside node_modules — any change is lost the next time you run npm install.
  • Committing node_modules to git, which bloats a repository unnecessarily.
  • Deleting package-lock.json "to clean things up," which can silently change dependency versions for everyone on the project.
  • Putting new components directly in src/ with no folder structure once a project has more than a handful of files.

Best Practices

  • Keep node_modules out of version control by leaving it in .gitignore.
  • Always commit package-lock.json alongside package.json so installs stay reproducible.
  • Create a src/components folder as soon as you add a second component.
  • Treat main.jsx as "wiring" — it should rarely need edits once your app is running.
  • Use App.jsx as a high-level layout that composes smaller components, rather than a place to put all your markup.

Frequently Asked Questions

Can I rename App.jsx to something else?

Yes, but you would also need to update the import in main.jsx to match. Keeping the conventional name App.jsx is recommended so other developers immediately recognize it.

Why is there only one HTML file in a React project?

React apps are typically single-page applications: one HTML shell is loaded once, and React swaps content in and out of it via JavaScript, without full page reloads.

What goes in the public/ folder versus src/assets?

public/ is for files that need a stable, predictable URL and are copied unchanged (like favicons). Files in src/assets are imported directly into your components and processed by Vite's build pipeline.

Do I have to use a components folder?

No, it is a convention rather than a requirement. But nearly every real-world React codebase follows some version of it, so it is worth adopting early.

Is it safe to delete node_modules?

Yes — it can always be regenerated by running npm install again, since package.json and package-lock.json describe exactly what should be inside it.

Key Takeaways

  • A Vite React project has one real HTML page, index.html, with a single #root container.
  • main.jsx is the entry point that mounts your App component into the DOM using createRoot().render().
  • App.jsx is the root component that sits at the top of your component tree.
  • New components conventionally live in a src/components folder as a project grows.
  • node_modules holds installed packages; package-lock.json pins their exact versions for reproducible installs.

Summary

What looked like an unfamiliar pile of generated files is actually a small, consistent structure: one HTML shell, an entry point that mounts React, a root component, and configuration around the edges. Understanding this layout now means every file you touch later has a clear purpose.

In this lesson, you toured a Vite React project folder by folder, learned what main.jsx and App.jsx each do, saw where new components belong, and learned what node_modules and package-lock.json are for. Next, you will dig into JSX itself — the syntax that makes files like App.jsx possible.

Lesson 8 Completed
  • You can navigate a Vite React project confidently.
  • You understand the role of index.html, main.jsx, and App.jsx.
  • You know where new components should live as a project grows.
  • You understand what node_modules and package-lock.json are for.
Next Lesson →

JSX (JavaScript XML)