Lesson 1 of 13 · React basics
Why React: components, declarative rendering and a first project with Vite
Objective
Understand the problem React solves, how it updates the screen, and where it fits in the ecosystem (Vite, Next.js, React Router). Then build the structure of a Vite project by hand, so you know what each file contains.
Key points
- React is a JavaScript library for building user interfaces from components: functions that return markup.
- Rendering is declarative: you describe the screen for a given state; React updates the DOM when the state changes.
- React works in two phases: render (it calls your components to compute the result), then commit (it applies only the differences to the DOM).
- React "only" handles the user interface. For tooling, you use Vite (development server, build) or a framework such as Next.js or React Router.
- A Vite project has four pieces:
package.json,index.html,src/main.jsx(entry point) andsrc/App.jsx(first component).
The problem: updating the screen by hand
Take the Tifawin rug cooperative in Azilal, which sells online. In the "Web" course, you wrote this kind of code to show the number of items in the cart:
// "Imperative" JavaScript: you tell the browser WHAT TO CHANGE, step by step
let nombre = 0;
const badge = document.querySelector('#badge-panier');
const bouton = document.querySelector('#ajouter');
bouton.addEventListener('click', () => {
nombre = nombre + 1;
badge.textContent = nombre; // don't forget to update the badge…
if (nombre > 0) badge.classList.remove('cache'); // …and its visibility…
// …and the total, and the "Order" button, and the summary at the bottom of the page…
});
With three elements on the screen, everything is fine. With a catalog, filters, a cart and an order form, each event must remember every area to update. Anything you forget becomes a bug: the badge shows 3, the summary shows 2.
React's answer: describe, don't modify
With React, you write what the screen should be for a given value. When the value changes, React recomputes and updates the DOM for you:
import { useState } from 'react';
export default function BoutonPanier() {
const [nombre, setNombre] = useState(0); // the state: the component's "memory"
return (
<div>
<button onClick={() => setNombre(nombre + 1)}>Ajouter au panier</button>
{nombre > 0 && <span className="badge">{nombre}</span>}
</div>
);
}
There is no more querySelector or textContent: the user interface is a function of the state. You can sum it up with a formula: UI = f(state).
Components: reusable building blocks
A component is a JavaScript function whose name starts with a capital letter and that returns markup (JSX, covered in the next lesson). You assemble components like building blocks:
function CarteProduit({ nom, prix }) {
return (
<article>
<h2>{nom}</h2>
<p>{prix} DH</p>
</article>
);
}
export default function Catalogue() {
return (
<main>
<CarteProduit nom="Tapis Azilal 2 x 3 m" prix={4200} />
<CarteProduit nom="Coussin en cuir de Fès" prix={350} />
</main>
);
}
The whole application becomes a component tree: App contains Entete, Catalogue and Panier; Catalogue contains CarteProduit components, and so on.
Render and commit: what people call the "virtual DOM"
You often hear that React uses a "virtual DOM". In practice, an update happens in three steps:
- Trigger: at startup (initial render), or when a state changes (
setNombre(...)). - Render: React calls your components. They return a lightweight description of the UI (JavaScript objects, not real DOM nodes). React compares this description with the one from the previous render: this is reconciliation.
- Commit: React applies only the differences to the real DOM. If only the badge changed, only the badge text is modified.
Practical consequence: a text field where the user is typing is not "erased" by a new render, because React does not touch what has not changed.
setNombre(3)
│
▼
Render : BoutonPanier() is called again → <span>3</span> instead of <span>2</span>
│
▼
Commit : a single change in the DOM, the text of the <span>
│
▼
The browser repaints the screen
Remember: "rendering" means calling your components, not necessarily touching the DOM.
The ecosystem: React does not come alone
React takes care of the UI. A real application also needs to transform JSX, bundle files, manage pages and sometimes render on the server. So you choose a tool around it:
| Tool | Role | When to choose it |
|---|---|---|
| Vite | very fast development server, production build | "client-side" application (SPA): dashboard, back office, shop served as static files |
| Next.js | full framework: pages, server rendering, API routes | public site that needs good search engine ranking, server rendering, large project |
| React Router | navigation between pages; can also act as a full framework | SPA with several pages (lesson 2.5) |
In this course, you use Vite: it is the simplest way to learn, and the result (HTML, CSS and JS files) can be deployed on any nginx server.
React can also be added to part of an existing site (for example, a booking widget in a PHP page): you then mount React in a single element of the page.
Creating a Vite project (with network access)
On your machine, with Node.js 20 or later (see the "Node.js" course):
npm create vite@latest boutique -- --template react # generates the project
cd boutique
npm install # downloads react, react-dom, vite…
npm run dev # development server on http://localhost:5173
Each time you save a file, the page updates without reloading (hot reload).
What the project contains
boutique/
├── package.json # dependencies and scripts
├── index.html # the only HTML page: an empty div + the entry script
├── vite.config.js # Vite configuration (React plugin)
├── public/ # files copied as they are (favicon, robots.txt)
└── src/
├── main.jsx # entry point: mounts React in the div
├── App.jsx # root component
└── index.css
package.json
{
"name": "boutique-artisanat",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^5.0.0",
"vite": "^7.0.0"
}
}
reactcontains components, JSX and hooks;react-domknows how to display them in a browser.viteand its plugin are only used for development and build: they aredevDependencies.
index.html
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Coopérative Tifawin</title>
</head>
<body>
<div id="root"></div> <!-- React will display everything here -->
<script type="module" src="/src/main.jsx"></script> <!-- Vite transforms the JSX -->
</body>
</html>
src/main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
);
createRoot takes control of div#root; render displays the App component in it. StrictMode enables extra checks in development only (for example, it calls some code twice to reveal errors).
src/App.jsx
export default function App() {
return <h1>Coopérative Tifawin</h1>;
}
Setting up your workstation
- Editor: VS Code (or equivalent) with ESLint and Prettier.
- React Developer Tools extension in Chrome, Firefox or Edge: it adds the Components and Profiler tabs to the developer tools (lesson 3.2).
- Node.js LTS and an up-to-date npm.
Common pitfalls
- Lowercase component name:
<carteProduit />is treated as an unknown HTML tag. A component always starts with a capital letter. - Opening
index.htmldirectly in the browser (double-click): the JSX is not transformed, and the page stays blank. Usenpm run dev. - Putting
viteindependencies: it works, but it is not clean. Build tools go indevDependencies. - Believing that React replaces JavaScript: everything you have learned (arrays, objects,
map,filter, modules, promises) is used in every line of React.
Lab
The lab has no network access: you build by hand the exact skeleton that npm create vite would have produced, for the cooperative's shop:
- the
package.jsonas ES modules; - the
dev,buildandpreviewscripts; - the React and Vite dependencies (declared, not installed);
- the
index.htmlpage; - the
main.jsxentry point and theApp.jsxcomponent.
Quiz
- What does "declarative rendering" mean?
- What does React do during the render phase, and during the commit?
- Why is
vitea development dependency? - What is the
<div id="root">element ofindex.htmlfor? - When should you prefer Next.js over a Vite application?
Answers
- You describe the expected UI for a given state; React computes and applies the DOM changes itself.
- Render: it calls the components and compares the result with the previous render. Commit: it applies only the differences to the DOM.
- It is only used to develop and build the files; the production site does not need it.
- It is the container in which
createRoot(...).render(<App />)displays the whole application. - For a public site that needs server-side rendering (search engine ranking, fast first display) or server routes; Vite suits client-side applications served as static files.
Reference sources: React documentation in French (fr.react.dev, CC BY 4.0 license). Original MasterLinux text.