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.html ➡ main.jsx ➡ App.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>
There are two critical points here:
<div id="root"></div>: Thisdivis empty. It is the container node. React will take full control of this element and render the entire application inside it.<script type="module" ...>: This is where our JavaScript code is loaded. Notice it usestype="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>,
)
Let’s analyze what happens line by line:
- Imports: Brings in the
ReactandReactDOMlibraries (necessary for the web). document.getElementById('root'): Looks for that emptydivwe saw in the HTML.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
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
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 theimportstatements for the logos andApp.css. - In
main.jsx: Make sure everything is still correct.
