React is, today, the most widely used library for building web user interfaces, and understanding what problem it solves matters far more than memorizing its API. Before writing a single line of code, it's worth being clear on why thousands of teams abandoned manual DOM manipulation and adopted a declarative, component-based model. In this lesson you'll see that contrast through a real example, get acquainted with React's basic vocabulary, and meet CicloUrbano, the bike rental app you'll build throughout the course.
Contents
- The problem: manipulating the DOM by hand
- The same screen with React: the declarative approach
- What React actually is (and isn't)
- The interface as a function of state
- A brief history and where things stand today
- The vocabulary you'll see from now on
- When React fits and when it doesn't
- Introducing the project: CicloUrbano
- The problem: manipulating the DOM by hand
Picture a very simple CicloUrbano screen: a list of bikes with their model, status and price per hour, plus a filter to show only the available ones. With plain JavaScript and direct DOM manipulation, the code looks something like this.
// catalogue.js — plain JavaScript version, with direct DOM manipulation
const bikes = [
{ id: 'bici-001', model: 'Classic Urban', type: 'urbana', status: 'disponible', pricePerHour: 2.5 },
{ id: 'bici-002', model: 'Electric Pro', type: 'electrica', status: 'alquilada', pricePerHour: 4 },
{ id: 'bici-003', model: 'Cargo Max', type: 'carga', status: 'mantenimiento', pricePerHour: 5.5 }
];
let availableOnly = false;
const container = document.getElementById('catalogue');
const button = document.getElementById('filter');
const counter = document.getElementById('counter');
function renderCatalogue() {
// 1. You have to manually clear whatever was already rendered
container.innerHTML = '';
// 2. You have to manually decide what gets shown
const visible = availableOnly
? bikes.filter((b) => b.status === 'disponible')
: bikes;
// 3. You have to build every DOM node step by step
visible.forEach((bike) => {
const card = document.createElement('article');
card.className = 'bike-card';
const title = document.createElement('h3');
title.textContent = bike.model;
const status = document.createElement('span');
status.className = 'status status--' + bike.status;
status.textContent = bike.status;
const price = document.createElement('p');
price.textContent = bike.pricePerHour.toFixed(2) + ' €/h';
card.append(title, status, price);
container.append(card);
});
// 4. And you have to remember to update EVERYTHING else that depends on the data
counter.textContent = visible.length + ' bikes';
}
button.addEventListener('click', () => {
availableOnly = !availableOnly;
button.textContent = availableOnly ? 'View all' : 'Available only';
renderCatalogue();
});
renderCatalogue();This code works. The problem isn't its length — it's what happens to it as the application grows:
- Manual synchronization. The data lives in
bikesandavailableOnly, but the screen lives in the DOM. Nothing guarantees they agree with each other — it's on you to callrenderCatalogue()at exactly the right moment. The day a teammate adds a new action and forgets that call, the screen will show stale information. - Instructions, not descriptions. The code doesn't say what the screen should look like; it says what steps to take to get there (
createElement,append,textContent...). Reading it forces you to mentally simulate its execution. - Wiping and repainting everything is crude.
container.innerHTML = ''destroys and recreates everything: keyboard focus, scroll position, and the state of any form field inside are all lost. - Multiplicative complexity. With one filter, it's manageable. Add a search box, a sort order, a counter, a station badge and a booking button, and the number of combinations you have to keep consistent grows far faster than the number of features.
- The same screen with React: the declarative approach
Now here's the same functionality written with React. You don't need to understand every detail yet — just pay attention to the shape of the code.
// BikeCatalogue.jsx — React version
import { useState } from 'react';
const bikes = [
{ id: 'bici-001', model: 'Classic Urban', type: 'urbana', status: 'disponible', pricePerHour: 2.5 },
{ id: 'bici-002', model: 'Electric Pro', type: 'electrica', status: 'alquilada', pricePerHour: 4 },
{ id: 'bici-003', model: 'Cargo Max', type: 'carga', status: 'mantenimiento', pricePerHour: 5.5 }
];
function BikeCatalogue() {
const [availableOnly, setAvailableOnly] = useState(false);
const visible = availableOnly
? bikes.filter((b) => b.status === 'disponible')
: bikes;
return (
<section>
<button onClick={() => setAvailableOnly(!availableOnly)}>
{availableOnly ? 'View all' : 'Available only'}
</button>
<p>{visible.length} bikes</p>
{visible.map((bike) => (
<article className="bike-card" key={bike.id}>
<h3>{bike.model}</h3>
<span className={`status status--${bike.status}`}>{bike.status}</span>
<p>{bike.pricePerHour.toFixed(2)} €/h</p>
</article>
))}
</section>
);
}
export default BikeCatalogue;Compare both versions side by side:
| Aspect | Plain JavaScript (imperative) | React (declarative) |
|---|---|---|
| What you write | The steps to modify the screen | How the screen should look for given data |
| Who updates the DOM | You, with createElement, append, innerHTML |
React, by comparing the new output with the previous one |
| Where the truth lives | Split between variables and the DOM | In the component's state, and only there |
| When data changes | You must remember to call the render function | React re-runs the component automatically |
| Risk of falling out of sync | High and growing | Practically eliminated by design |
| Reusing the card elsewhere | Copy and paste the function that builds it | Extract a component and use it wherever needed |
The key difference boils down to a single sentence: the imperative version describes transitions; the declarative version describes destinations. You declare, "given this data, the screen looks like this," and React works out what needs to change in the DOM to get there. Exactly how it works that out is the subject of the lesson How React Renders: Virtual DOM and Reconciliation.
- What React actually is (and isn't)
React is a JavaScript library for building user interfaces using components. It's worth unpacking that definition:
- A library, not a framework. React focuses on one thing: producing and keeping the UI up to date. It doesn't ship a router, an HTTP client, a forms system, a global state manager, or a folder convention. You choose those pieces from the ecosystem yourself (you'll see React Router in Module 6 and Redux in Module 7). That means more freedom, and also more decisions.
- Declarative. You describe the outcome, not the procedure, as you just saw.
- Component-based. The unit of construction is the component: a function that returns a piece of UI. Components nest inside one another to form a tree, much like HTML tags, but with the advantage that they're your own pieces — named, reusable, and testable in isolation.
- Agnostic about the target. React describes the interface; something else — a renderer — paints it.
react-dompaints it in the browser, React Native paints it with native iOS and Android components (you'll see this in 10-05), and there are renderers for terminals and PDFs too. That's why thereactpackage and thereact-dompackage are separate.
What React is not: it isn't a new language (it's JavaScript), it isn't a substitute for HTML and CSS (you still need both), and it isn't a magic performance solution (a poorly written React app can run slower than a well-built static page).
- The interface as a function of state
The idea underpinning all of React fits into one conceptual expression:
In other words: the screen is the result of applying your components to the current data. If the state is "the filter is off and there are three bikes," there is one screen, and only one. If the state changes to "filter on," React conceptually recalculates the screen from scratch and updates the real DOM with the differences.
flowchart LR
A[State<br/>application data] --> B[Components<br/>functions that describe the interface]
B --> C[Interface description]
C --> D[React updates the real DOM]
E[User interaction] --> F[State change]
F --> A
The practical consequences of this model are huge:
- There's only one source of truth. If you want to know why the screen looks the way it does, you look at the state. There's no third place where someone quietly modified a
textContenton their own. - Debugging gets easier. Reproducing a bug becomes reproducing a state.
- The flow is predictable. Interaction → state change → new render. Always in that order, always in that direction.
- A brief history and where things stand today
| Year | Milestone |
|---|---|
| 2011-2012 | Jordan Walke builds an internal prototype at Facebook to solve UI synchronization problems in the feed and in ads |
| 2013 | Released as open source. JSX and the Virtual DOM meet initial skepticism |
| 2015 | React Native brings the model to native mobile |
| 2016 | React 15: consolidation and mass adoption across the industry |
| 2017 | React 16 ("Fiber"): a complete rewrite of the internal engine, error boundaries, fragments |
| 2019 | React 16.8 introduces hooks: function components can hold state, and the class model is sidelined |
| 2022 | React 18: concurrent rendering, createRoot, transitions, server-side Suspense |
| 2024-2025 | React 19: stable Server Components, new actions and forms APIs, an optimizing compiler |
Facts worth keeping in mind:
- It's maintained by Meta along with a large group of outside contributors, and it's released under the MIT license.
- It's the most in-demand front-end technology in the web job market, which translates into abundant documentation, courses, component libraries, and answers to concrete problems.
- The official documentation (
react.dev) was rewritten from scratch around hooks: if you find a tutorial full ofclass ... extends React.Component, it's almost certainly outdated. In this course, class components appear only as legacy code you need to know how to read (lesson 02-02).
- The vocabulary you'll see from now on
These four terms run through the whole course. Here we just name them; each one gets its own lesson.
| Concept | In one sentence | Where it's covered |
|---|---|---|
| Component | A function that returns a piece of UI and gets reused like its own custom tag | Module 2 |
| JSX | HTML-like syntax written inside JavaScript to describe the interface | Lesson 01-04 |
| Props | Data a parent component passes down to a child; read-only | Lesson 02-03 |
| State | Data a component holds across renders that, when it changes, triggers a new render | Lesson 02-04 |
| Virtual DOM | A lightweight in-memory representation of the interface that React compares to work out what to touch in the real DOM | Lesson 01-05 |
- When React fits and when it doesn't
React shines when the interface is state-rich: lots of data changing, lots of views that depend on it, and lots of interaction. A CicloUrbano operator panel that shows in real time which bikes are rented out is a textbook case. A three-page static brochure is not.
| Option | Model | Strength | Watch out for |
|---|---|---|---|
| React | Declarative component library | Huge ecosystem and community, employability, React Native | You have to assemble the rest of the stack yourself, choosing libraries |
| Vue | Progressive, template-based framework | Gentle learning curve, excellent documentation | Smaller community than React's |
| Angular | Full, opinionated framework with TypeScript | Everything comes included and standardized; great fit for large organizations | Much more to learn before you're productive |
| Svelte | Compiler, no Virtual DOM runtime | Less code and very lightweight bundles | Smaller ecosystem and job market |
| No framework | DOM + standard JavaScript | Zero dependencies, maximum control, minimal payload | The whole synchronization burden falls on you |
Signs you don't need React:
- The page is essentially content with little or no interaction.
- SEO and first-load time are critical, and you're not going to set up server-side rendering.
- The team is very small and the functionality fits into a handful of event handlers.
- A server-side solution (templates) already solves the problem with fewer moving parts.
Signs you do:
- There's shared state across several areas of the screen that needs to stay in sync.
- The interface is made up of pieces that repeat with different data (cards, rows, filters, modals).
- You expect the app to grow, with several people working on the same code.
- You need frequent updates without reloading the page.
- Introducing the project: CicloUrbano
Throughout the course you'll build CicloUrbano, a fictional app for renting urban bikes by the station. Each lesson adds a piece, so that by the time you reach Module 11 you'll have a complete application rather than a collection of disconnected examples.
The domain
// src/data/domain.js — the shape of the data we'll use throughout the course
// Bike: { id, model, type, status, stationId, pricePerHour }
// type: 'urbana' | 'electrica' | 'carga'
// status: 'disponible' | 'alquilada' | 'mantenimiento'
export const bikes = [
{ id: 'bici-001', model: 'Classic Urban', type: 'urbana', status: 'disponible', stationId: 'est-01', pricePerHour: 2.5 },
{ id: 'bici-002', model: 'Electric Pro', type: 'electrica', status: 'alquilada', stationId: 'est-01', pricePerHour: 4.0 },
{ id: 'bici-003', model: 'Cargo Max', type: 'carga', status: 'mantenimiento', stationId: 'est-02', pricePerHour: 5.5 },
{ id: 'bici-004', model: 'Classic Urban', type: 'urbana', status: 'disponible', stationId: 'est-03', pricePerHour: 2.5 },
{ id: 'bici-005', model: 'Electric Pro', type: 'electrica', status: 'disponible', stationId: 'est-02', pricePerHour: 4.0 }
];
// Station: { id, name, district, docks }
export const stations = [
{ id: 'est-01', name: 'Main Square', district: 'Downtown', docks: 20 },
{ id: 'est-02', name: 'North Park', district: 'North', docks: 15 },
{ id: 'est-03', name: 'Central Station', district: 'Riverside', docks: 30 }
];
// User: { id, name, email, role } -> role: 'cliente' | 'operario'
export const users = [
{ id: 'usr-01', name: 'Ana Ribera', email: '[email protected]', role: 'cliente' },
{ id: 'usr-02', name: 'Marc Solé', email: '[email protected]', role: 'operario' }
];
// Booking: { id, bicicletaId, user, startDate, hours, status }
export const bookings = [
{ id: 'res-01', bicicletaId: 'bici-002', user: 'usr-01', startDate: '2026-05-04T09:00', hours: 2, status: 'activa' }
];All the data is fictional: the names, emails and stations are invented for the course. Never use real people's data when you practice.
The features we'll build
flowchart TD
A[CicloUrbano] --> B[Bike catalogue<br/>Modules 1-3]
A --> C[Filters, search and forms<br/>Module 3]
A --> D[Shared state and hooks<br/>Modules 4-5]
A --> E[Navigation between pages<br/>Module 6]
A --> F[Global state and server data<br/>Module 7]
A --> G[Performance and testing<br/>Modules 8-9]
A --> H[Complete, deployed application<br/>Modules 10-11]
In product terms, CicloUrbano will let you:
- Browse the bike catalogue with each bike's model, type, status and price per hour.
- Filter by type, status and station, and search by model.
- View a station's details, including its bikes and free docks.
- Create a booking through a validated form.
- View the user's list of bookings and cancel them.
- Provide an operator panel with role-restricted access.
Common Mistakes and Tips
- Thinking React replaces HTML, CSS or JavaScript. It doesn't. The better you know the DOM, the CSS cascade and promises, the better your React code will be. If any part of ES6+ feels shaky, brush up on it now: destructuring, spread,
map/filterand arrow functions show up in literally every lesson. - Trying to touch the DOM "by hand" inside a component. A
document.getElementById(...).innerHTML = ...inside React clashes directly with what React believes is rendered. There are legitimate ways to access the DOM when you truly need to (useRef, lesson 05-03), but they're the exception. - Choosing React just because it's trendy. Justify the decision: is the state rich? Does the interface change a lot? Is the team going to grow? If the answer to all three is no, you might not need it.
- Learning by copying instead of writing. Type out the examples in this course in your own project. The muscle memory for JSX syntax comes from writing it, not reading it.
- Confusing "declarative" with "automatic". React automates DOM updates, not the design of your state. Modeling your data well is still your job, and it's the decision that will have the biggest impact on code quality.
Exercises
Exercise 1
In the imperative snippet from section 1, identify the four distinct responsibilities the programmer takes on that, in the React version, disappear or get taken over by the library. Write them in your own words.
Exercise 2
For each of these four projects, decide whether you'd use React or not, and justify your answer in two or three lines:
- CicloUrbano's legal notice page: static text, no interaction.
- An operator panel that shows the status of 200 bikes and refreshes every 10 seconds.
- A four-field contact form on a corporate website built with server-side templates.
- CicloUrbano's customer-facing app: catalogue, filters, bookings and personal area.
Exercise 3
Write the JavaScript object for a new CicloUrbano bike that meets all these conditions, matching exactly the shape of the domain defined in section 8: it's the "Cargo Max" model, it's available, it's located at the "Central Station" station, and it costs €5.50 an hour. Then write the expression that, starting from the bikes array, returns only the available bikes at station est-01.
Solutions
Solution 1.
- Building the DOM node by node: creating each element, assigning classes and text, and attaching them to the container. In React, you describe the result with JSX and the library creates the nodes.
- Clearing the previous output before repainting (
innerHTML = ''). React calculates the differences and only changes what's changed, without destroying the rest. - Deciding when to repaint: remembering to call
renderCatalogue()after every change. In React, changing the state triggers the new render automatically. - Keeping secondary elements in sync (the counter and the button's text). In React, both are expressions derived from the state, so they can never fall out of sync.
Solution 2.
- No. Static content with no state: HTML and CSS are enough. Adding React increases load and maintenance cost without adding anything.
- Yes. Plenty of state, constantly changing and shared across several areas of the screen: exactly the scenario React was designed for.
- Probably not. A standalone form inside a server-rendered website can be handled with standard JavaScript. Bringing in React would mean setting up an entire build process for very little gain. If, down the line, there were many forms with complex validation, the decision could change.
- Yes. Many views sharing data, navigation between pages, constant interaction and a team that will grow: React brings structure, reusability and an ecosystem.
Solution 3.
// New bike matching the shape of the domain
const newBike = {
id: 'bici-006',
model: 'Cargo Max',
type: 'carga',
status: 'disponible',
stationId: 'est-03', // 'Central Station'
pricePerHour: 5.5
};
// Available bikes at station est-01 ('Main Square')
const availableAtMainSquare = bikes.filter(
(b) => b.status === 'disponible' && b.stationId === 'est-01'
);
// -> [{ id: 'bici-001', model: 'Classic Urban', ... }]Notice that type is inferred from the model ("Cargo Max" is of type 'carga'), and that the station is referenced by its id, never by its name: identifiers are stable, while names could change.
Conclusion
React solves one very specific problem: keeping the interface in sync with the data without you having to orchestrate every DOM update yourself. It does this with a declarative model ("this is what the screen looks like given this data") that's component-based (named pieces that combine and get reused), summed up in the idea that the interface is a function of state. It's a library, not a framework: it gives you the UI layer and lets you choose the rest. It fits when there's rich state and constant interaction, and it's overkill when the page is essentially static content.
You've also now met CicloUrbano, its domain, and the sample data that will reappear in every lesson of the course.
In the next lesson, Setting Up the Development Environment, you'll move from theory to practice: you'll install Node.js, create the CicloUrbano project with Vite, understand what each generated folder is for, and get the development server running with hot reload.
React Course
Module 1: Getting Started with React
- What Is React?
- Setting Up the Development Environment
- Hello World in React
- JSX: A JavaScript Syntax Extension
- How React Renders: Virtual DOM and Reconciliation
Module 2: React Components
- Understanding Components
- Function vs Class Components
- Props: Passing Data to Components
- State: Managing Component State
- Styling Components: CSS, Modules and Utilities
Module 3: Working with Events
- Handling Events in React
- Conditional Rendering
- Lists and Keys
- Forms and Controlled Components
- Form Validation and Uncontrolled Components
- Accessibility in Interactive Components
Module 4: Advanced Component Concepts
- Lifting State Up
- Composition vs Inheritance
- React Lifecycle Methods
- Hooks: Introduction and Basic Use
- Error Boundaries: Catching Failures in the UI
Module 5: React Hooks
- The useState Hook
- The useEffect Hook
- The useRef Hook and DOM Access
- The useContext Hook
- The useReducer Hook
- Custom Hooks
Module 6: Routing in React
- Introducing React Router
- Setting Up React Router
- Nested Routes
- Programmatic Navigation
- Protected Routes and Access Control
Module 7: State Management
- Introduction to State Management
- The Context API
- Redux: Introduction and Setup
- Redux: Actions and Reducers
- Redux: Connecting to React
- Server State: Fetching, Caching and Syncing
Module 8: Performance Optimization
- Performance Optimization Techniques in React
- Memoization with React.memo
- The useMemo and useCallback Hooks
- Code Splitting and Lazy Loading
- Measuring Performance with React DevTools Profiler
Module 9: Testing React Applications
- Introduction to Testing
- Unit Testing with Jest
- Component Testing with React Testing Library
- Testing Asynchronous Code and Mocking APIs
- End-to-End Testing with Cypress
Module 10: Advanced Topics
- Server-Side Rendering (SSR) with Next.js
- Static Site Generation (SSG) with Next.js
- Suspense and React Server Components
- TypeScript with React
- React Native: Building Mobile Apps
