react-estructura-proyecto-vite

Structure of a React Project

  • 5 min

Once we have executed the Vite creation command (npm create vite@latest) and installed the dependencies, we find ourselves with a folder structure that, if you come from other environments, might be a bit intimidating at first.

In this article, we are going to dissect the anatomy of a React project.

Finally, we will perform a “cleanup” to leave the project ready to start coding from scratch, removing the example code that comes with the template.

Folder structure

If we open our code editor (VS Code, of course), we will see something like this:

mi-app-react/ ├── node_modules/ # Installed dependencies (the black hole) ├── public/ # Public static files │ └── vite.svg ├── src/ # Source code (where we will work 99% of the time) │ ├── assets/ # Importable images, fonts, etc. │ ├── App.css # Styles for the App component │ ├── App.jsx # The main component │ ├── index.css # Global styles │ └── main.jsx # JavaScript entry point ├── .eslintrc.cjs # Linter configuration ├── index.html # Application entry point ├── package.json # Project manifest and scripts └── vite.config.js # Vite configuration

Public vs Src

The difference between these two folders:

  • public: Files here are not processed by Vite. They are copied as-is to the server root. Used for favicon.ico, robots.txt, or images that won’t change and we want to reference by absolute URL.
  • src: This is where our code lives. Everything here will be processed, minified, and bundled by Vite.

The execution flow

To understand how React starts up, we must follow the thread from the browser. The order of execution is: index.htmlmain.jsxApp.jsx.

The Host: index.html

Unlike older tools, in Vite the index.html lives at the project root. It is the first file the server serves.

If you open it, you will see this:

<!doctype html>
<html lang="en">
  <body>
    <div id="root"></div>
    
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>
Copied!

There are two critical points here:

  1. <div id="root"></div>: This div is empty. It is the container node. React will take full control of this element and render the entire application inside it.
  2. <script type="module" ...>: This is where our JavaScript code is loaded. Notice it uses type="module", taking advantage of native ES6 modules.

The Startup: src/main.jsx

This is the entry point of the logic. Its mission is to connect React to the HTML.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
Copied!

Let’s analyze what happens line by line:

  1. Imports: Brings in the React and ReactDOM libraries (necessary for the web).
  2. document.getElementById('root'): Looks for that empty div we saw in the HTML.
  3. createRoot(...).render(...): This is the modern React 18 API. It creates a React “root” in that node and tells it: “Render the <App /> component inside here”.

What is StrictMode? You’ll see that <App /> is wrapped in <React.StrictMode>. It is a development tool that activates extra checks to detect errors and unwanted side effects. It can cause additional renders and repeat the setup/cleanup cycle of effects. It does not affect production, but if you see duplicated console.log statements, this is why.

The Root Component: src/App.jsx

Finally, we reach App.jsx. This is our first Functional Component.

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      {/* ... a bunch of example HTML ... */}
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  )
}

export default App
Copied!

As we saw in the introduction, a component is nothing more than a JavaScript function that returns JSX (that syntax that looks like HTML but isn’t).

Initial Cleanup

The Vite template is great for verifying everything works, but for our course we want a blank canvas. Let’s clean up the project.

Clean src/App.jsx

Delete all the content inside the return and the state logic, leaving it in its minimal form:

// src/App.jsx cleaned
function App() {
  return (
    <div>
      <h1>Hello World from React</h1>
    </div>
  )
}

export default App
Copied!

Remove unnecessary files

We can safely delete:

  • src/assets/react.svg (we don’t need the logo).
  • src/App.css (we’ll start styling from scratch or use another strategy).

Clean src/index.css

You can delete all the content to remove Vite’s default styles, or leave only the basics (a simple reset). I usually recommend emptying it so it doesn’t interfere with what we learn later.

Fix the imports

After deleting files, main.jsx or App.jsx will complain because they try to import things that no longer exist.

  • In App.jsx: Remove the import statements for the logos and App.css.
  • In main.jsx: Make sure everything is still correct.