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

  1. The problem: manipulating the DOM by hand
  2. The same screen with React: the declarative approach
  3. What React actually is (and isn't)
  4. The interface as a function of state
  5. A brief history and where things stand today
  6. The vocabulary you'll see from now on
  7. When React fits and when it doesn't
  8. Introducing the project: CicloUrbano

  1. 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 bikes and availableOnly, but the screen lives in the DOM. Nothing guarantees they agree with each other — it's on you to call renderCatalogue() 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.

  1. 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.

  1. 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-dom paints 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 the react package and the react-dom package 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).

  1. The interface as a function of state

The idea underpinning all of React fits into one conceptual expression:

interface = f(state)

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 textContent on 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.

  1. 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 of class ... 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).

  1. 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

  1. 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.

  1. 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:

  1. Browse the bike catalogue with each bike's model, type, status and price per hour.
  2. Filter by type, status and station, and search by model.
  3. View a station's details, including its bikes and free docks.
  4. Create a booking through a validated form.
  5. View the user's list of bookings and cancel them.
  6. 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/filter and 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:

  1. CicloUrbano's legal notice page: static text, no interaction.
  2. An operator panel that shows the status of 200 bikes and refreshes every 10 seconds.
  3. A four-field contact form on a corporate website built with server-side templates.
  4. 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.

  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.
  2. Clearing the previous output before repainting (innerHTML = ''). React calculates the differences and only changes what's changed, without destroying the rest.
  3. Deciding when to repaint: remembering to call renderCatalogue() after every change. In React, changing the state triggers the new render automatically.
  4. 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.

  1. No. Static content with no state: HTML and CSS are enough. Adding React increases load and maintenance cost without adding anything.
  2. Yes. Plenty of state, constantly changing and shared across several areas of the screen: exactly the scenario React was designed for.
  3. 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.
  4. 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

Module 2: React Components

Module 3: Working with Events

Module 4: Advanced Component Concepts

Module 5: React Hooks

Module 6: Routing in React

Module 7: State Management

Module 8: Performance Optimization

Module 9: Testing React Applications

Module 10: Advanced Topics

Module 11: Project: Building a Complete Application

© Copyright 2026. All rights reserved