Módulo 00: Introducción a React

Objetivos del módulo

  • Entender qué es React y qué problema resuelve, comparando el DOM manipulado a mano con una interfaz descrita de forma declarativa
  • Saber qué ocurre por dentro cuando cambia el estado: render, reconciliación y commit
  • Distinguir los conceptos básicos que usarás durante todo el curso: componente, props, estado, hook y JSX
  • Probar React en una página HTML sin instalar nada
  • Conocer la historia de React y por qué ha llegado a ser como es
  • Comparar React con Angular, Vue y Svelte con criterios útiles
  • Situar las piezas del ecosistema: herramientas, rutas, datos, estado, formularios, pruebas y frameworks
  • Leer la política de versiones de React: semver, canales latest, canary y experimental, y parches retroportados
  • Comprender que React no tiene LTS y qué consecuencias prácticas tiene
  • Decidir qué versión usar en cada situación y comprobar cuál tienes instalada
  • Conocer las novedades de React 19.0, 19.1, 19.2 y 19.3, y saber qué hacer si usas una versión anterior
  • Interpretar las marcas 🆕 y 🔄 de este curso y orientarte en su mapa de módulos

1. Qué es React

React es una biblioteca de JavaScript para construir interfaces de usuario. La definición cabe en una línea, pero cada palabra importa:

  • Biblioteca: resuelve una parte concreta del problema, la interfaz. No trae rutas, ni peticiones al servidor, ni validación de formularios. Esas piezas las eliges tú, como verás en la sección 8.
  • De JavaScript: un componente de React es una función de JavaScript normal. Si sabes usar map, filter, la desestructuración y las funciones flecha, ya sabes buena parte de lo que hace falta. Si necesitas repasarlo, tienes el Curso de JavaScript.
  • Interfaces de usuario: su trabajo es mantener lo que se ve en pantalla de acuerdo con los datos de la aplicación.

La idea central es que divides la interfaz en componentes, piezas reutilizables que combinas como bloques. Una aplicación de gestión como la que irás construyendo en este curso podría tener un componente TablaEmpleados, otro FilaEmpleado, otro BuscadorEmpleados, y uno mayor que los reúne a todos en una página.

React se reparte en dos paquetes de npm:

Paquete Qué contiene Ejemplos
react El núcleo: cómo se definen los componentes y los hooks. No sabe nada del navegador useState, useEffect, Suspense, memo
react-dom El puente con el navegador: convierte tus componentes en nodos del DOM createRoot, createPortal, flushSync

Esa separación explica por qué existe React Native: los mismos conceptos, con otro puente que crea vistas nativas de Android e iOS en lugar de nodos del DOM.

📘 Concepto: El DOM (Document Object Model) es la representación en memoria de una página HTML: un árbol de nodos que JavaScript puede leer y modificar. Lo que ves en el navegador se pinta a partir de ese árbol. Si necesitas repasarlo, consulta el Curso de JavaScript.


2. El problema: mantener la pantalla al día

Para apreciar lo que aporta React conviene sentir antes el problema. Vas a construir un buscador de empleados sin ninguna biblioteca. Los datos son los primeros empleados de EMPRESA, la aplicación que sirve de hilo conductor del curso.

La página tiene cuatro partes: una caja de búsqueda, un contador, la lista de resultados y un mensaje para cuando no hay resultados.

<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <title>Empleados (DOM a mano)</title>
  </head>
  <body>
    <main class="buscador">
      <h1>Empleados</h1>
      <input id="buscar" placeholder="Buscar por nombre u oficio" />
      <p class="total" id="total"></p>
      <p class="vacio" id="vacio" hidden>Ningún empleado coincide con la búsqueda.</p>
      <ul id="lista"></ul>
    </main>
    <script>
      const EMPLEADOS = [
        { empId: 100, nombre: 'Ana', apellidos: 'García López', oficio: 'DIRECTOR GENERAL' },
        { empId: 101, nombre: 'Luis', apellidos: 'Martínez Ruiz', oficio: 'DIRECTOR' },
        { empId: 102, nombre: 'Marta', apellidos: 'Sánchez Gil', oficio: 'DIRECTOR' },
        { empId: 103, nombre: 'Pedro', apellidos: 'Gómez Díaz', oficio: 'DIRECTOR' },
        { empId: 104, nombre: 'Laura', apellidos: 'Fernández Moreno', oficio: 'DIRECTOR' },
        { empId: 105, nombre: 'Javier', apellidos: 'López Navarro', oficio: 'DIRECTOR' },
        { empId: 110, nombre: 'Carmen', apellidos: 'Ruiz Ortega', oficio: 'VENDEDOR' },
        { empId: 111, nombre: 'David', apellidos: 'Torres Romero', oficio: 'VENDEDOR' },
      ]

      const buscar = document.querySelector('#buscar')
      const total = document.querySelector('#total')
      const vacio = document.querySelector('#vacio')
      const lista = document.querySelector('#lista')

      function crearFila(e) {
        const li = document.createElement('li')
        const nombre = document.createElement('strong')
        nombre.textContent = `${e.nombre} ${e.apellidos}`
        const oficio = document.createElement('span')
        oficio.textContent = e.oficio
        li.append(nombre, oficio)
        return li
      }

      function actualizar() {
        const filtro = buscar.value.toLowerCase()
        const visibles = EMPLEADOS.filter((e) =>
          `${e.nombre} ${e.apellidos} ${e.oficio}`.toLowerCase().includes(filtro),
        )

        // 1. Vaciar la lista y volver a crearla
        lista.replaceChildren(...visibles.map(crearFila))
        // 2. Actualizar el contador
        total.textContent = `${visibles.length} de ${EMPLEADOS.length} empleados`
        // 3. Mostrar u ocultar el mensaje de vacío
        vacio.hidden = visibles.length > 0
        // 4. Mostrar u ocultar la lista
        lista.hidden = visibles.length === 0
      }

      buscar.addEventListener('input', actualizar)
      actualizar()
    </script>
  </body>
</html>

Funciona, y para una página de este tamaño es una solución razonable. Fíjate en lo que has tenido que escribir. El dato importante es uno solo, el texto de búsqueda, pero el código está lleno de instrucciones sobre cómo pasar de una pantalla a la siguiente: vacía esto, crea aquello, cambia este texto, oculta lo otro. A este estilo se le llama imperativo: das órdenes paso a paso.

Por qué esto escala mal

El ejemplo tiene una sola función actualizar, y por eso sigue siendo sencillo. Añade ahora lo que pediría una aplicación real:

  • Un botón para borrar un empleado. Hay que quitar su fila, actualizar el contador y, si era el último, mostrar el mensaje de vacío.
  • Un panel lateral con el empleado seleccionado. Si se borra, hay que cerrar el panel. Si se cambia el filtro y deja de verse, hay que decidir qué pasa.
  • Un indicador de carga mientras llegan los datos del servidor. Hay que ocultar la lista, deshabilitar la búsqueda y volver a activarla después, también si la petición falla.
  • Una columna con el total de salarios de los empleados visibles.

Cada novedad se cruza con todas las anteriores. El borrado debe saber del contador, del panel y del total. La carga debe saber de la lista y de la búsqueda. Con cinco situaciones posibles ya hay muchas transiciones entre ellas, y cada una es código que alguien debe escribir sin olvidarse de nada.

El error típico tiene un nombre: la interfaz se desincroniza. El contador dice «2 de 8 empleados», pero se ven tres filas, porque un camino del código actualizó la lista y olvidó el contador. Estos errores son difíciles de encontrar porque cada línea, leída por separado, es correcta. Lo que falla es la combinación.

📘 Concepto: El problema de fondo no es que el DOM sea difícil, sino que hay dos fuentes de verdad: los datos de tu programa y lo que hay en el DOM. Cada cambio obliga a actualizar ambos a mano y a mantenerlos de acuerdo. React elimina ese trabajo: tú cuidas los datos, y el DOM se deriva de ellos.


3. La respuesta de React: describir el resultado

Este es el mismo buscador en React, con TypeScript. En este módulo no necesitas entender cada detalle: los hooks llegan en el módulo 04, JSX en el módulo 02 y las listas en el módulo 06. Quédate con la forma general.

import { useState } from 'react'
import { EMPLEADOS } from './datos'
import './buscador.css'

export default function BuscadorEmpleados() {
  const [texto, setTexto] = useState('')

  // Lo que se ve se CALCULA a partir del estado. No se guarda aparte.
  const filtro = texto.toLowerCase()
  const visibles = EMPLEADOS.filter((e) =>
    `${e.nombre} ${e.apellidos} ${e.oficio}`.toLowerCase().includes(filtro),
  )

  return (
    <main className="buscador">
      <h1>Empleados</h1>
      <input
        placeholder="Buscar por nombre u oficio"
        value={texto}
        onChange={(evento) => setTexto(evento.target.value)}
      />
      <p className="total">{visibles.length} de {EMPLEADOS.length} empleados</p>
      {visibles.length === 0 ? (
        <p className="vacio">Ningún empleado coincide con la búsqueda.</p>
      ) : (
        <ul>
          {visibles.map((e) => (
            <li key={e.empId}>
              <strong>{e.nombre} {e.apellidos}</strong>
              <span>{e.oficio}</span>
            </li>
          ))}
        </ul>
      )}
    </main>
  )
}

Buscador de empleados en React con el texto dire, que muestra 6 de 8 empleados

Figura 1. El buscador en React. Se comporta igual que la versión hecha a mano.

Lee el código con calma. Hay tres diferencias con la versión anterior.

No hay ninguna instrucción que modifique el DOM. No aparecen createElement, textContent ni hidden. La función devuelve una descripción de cómo debe verse la pantalla, y esa descripción contempla todos los casos: si no hay resultados, un mensaje; si los hay, la lista.

El único dato guardado es texto. visibles, el contador y la decisión de mostrar el mensaje de vacío se calculan a partir de él cada vez. Como no se guardan aparte, no se pueden desincronizar. El contador no puede decir «2» si se ven tres filas, porque ambos salen del mismo array en la misma ejecución.

Toda la interfaz está en un solo sitio. Para saber qué aspecto tiene la pantalla en una situación dada no hay que seguir los eventos: basta leer el return.

A este estilo se le llama declarativo: declaras qué quieres obtener y otro se ocupa de cómo. Ya conoces ejemplos: en CSS escribes color: red sin decir cómo se pintan los píxeles, y en SQL escribes SELECT ... WHERE sin programar el recorrido de la tabla.

Comparación entre el flujo imperativo, con cuatro pasos manuales, y el declarativo, donde la pantalla se calcula a partir del estado

Figura 2. A mano decides cada cambio en el DOM. En React describes la pantalla para un estado dado.

Esta idea suele resumirse en una fórmula que conviene memorizar, porque explica casi todo lo que verás en el curso:

pantalla = componente(estado)

Un componente es una función: para un mismo estado, devuelve la misma descripción de la interfaz. Cuando el estado cambia, React vuelve a llamar a la función y obtiene la descripción nueva.

Comparación directa

Aspecto DOM a mano React
Qué escribes Las transiciones entre pantallas Cómo es la pantalla para cada estado
Fuentes de verdad Los datos y el DOM Solo los datos
Añadir un caso nuevo Revisar todas las transiciones que le afectan Añadirlo a la descripción
Error típico La interfaz se desincroniza Un estado mal diseñado, o datos duplicados
Reutilizar una pieza Funciones que crean nodos, a tu manera Componentes con una forma común
Coste Ninguna dependencia Unos 225 kB sin comprimir y unos 70 kB con gzip en este ejemplo, más una herramienta de compilación
Cuándo encaja Páginas con poca interactividad Interfaces con mucho estado

La cifra de tamaño es la del build de producción real de este laboratorio, con varias demostraciones incluidas. Sirve como orden de magnitud y deja claro que React tiene un coste. Para una página con un menú desplegable, sería excesivo.

⚠️ Importante: Declarativo no significa mágico. React se ocupa del DOM, pero las decisiones siguen siendo tuyas: qué datos guardar, dónde guardarlos y cómo dividir los componentes. La mayoría de los errores en React vienen de ahí, y por eso el módulo 04 dedica tanto espacio a la estructura del estado.


4. Qué pasa por dentro

Una duda razonable: si la función se ejecuta entera cada vez que cambia el estado, ¿se destruye y se vuelve a crear toda la página con cada letra que escribes? No, y entender por qué te evitará muchos malentendidos.

Render, reconciliación y commit

Cuando el estado cambia, ocurren tres cosas en orden:

  1. Disparo (trigger). Algo pide una actualización, por ejemplo setTexto('vend'). React toma nota.
  2. Render. React llama a tu componente, que devuelve una descripción nueva de la interfaz. Esa descripción no son nodos del DOM, sino objetos JavaScript sencillos que indican qué etiqueta, qué atributos y qué hijos debe haber. React compara la descripción nueva con la anterior. A esa comparación se le llama reconciliación.
  3. Commit. React aplica al DOM real solo las diferencias encontradas.

Tres fases: disparo, render con comparación de descripciones, y commit con los cambios mínimos en el DOM

Figura 3. El render calcula. Solo el commit toca el DOM, y solo en lo que ha cambiado.

📘 Concepto: A la representación en memoria que React compara se le suele llamar DOM virtual (virtual DOM). Es un nombre popular, no una API que vayas a usar. Lo importante es la idea: comparar dos descripciones ligeras y tocar el DOM real lo mínimo.

⚠️ Importante: En React, renderizar significa ejecutar tu componente, no pintar en pantalla. Un componente puede renderizarse sin que cambie ni un píxel, como verás a continuación. Esta confusión de vocabulario es muy frecuente al empezar. El ciclo completo se estudia en el módulo 05.

Comprobación: cuánto se toca el DOM

No hace falta creer esto por fe. Se puede medir con MutationObserver, una API del navegador que avisa de cada cambio en el DOM. He observado los dos buscadores en Chromium, con la lista completa de ocho empleados, en dos situaciones:

Acción Versión Nodos quitados Nodos puestos Textos cambiados ¿Las filas que siguen son los mismos nodos?
Escribir a (siguen los 8) DOM a mano 9 9 0 No
Escribir a (siguen los 8) React 0 0 0 Sí
Escribir vend (quedan 2) DOM a mano 9 3 0 No
Escribir vend (quedan 2) React 6 0 1 Sí

Los resultados se explican solos:

  • La versión a mano quita las 8 filas y el nodo de texto del contador, porque replaceChildren y textContent sustituyen siempre lo que hay. Por eso sale 9 aunque nada haya cambiado.
  • Con a, React ejecuta el componente, compara y descubre que el resultado es idéntico. No toca el DOM.
  • Con vend, React quita las 6 filas que sobran y cambia el texto del contador. Las dos filas que quedan son los mismos nodos de antes.

Esto no es solo una cuestión de eficiencia. Si un nodo sobrevive, conserva lo que el navegador guarda en él, como verás ahora.

El experimento del reloj

Este ejemplo clásico muestra lo que se pierde al sustituir nodos. Es una página con un reloj y una caja de texto. Cada segundo se vuelve a pintar todo con innerHTML, la forma más cómoda de escribir código «casi declarativo» sin bibliotecas:

<div id="raiz"></div>
<script>
  const raiz = document.querySelector('#raiz')

  function pintar() {
    const hora = new Date().toLocaleTimeString('es-ES')
    raiz.innerHTML = `
      <h2>Son las ${hora}</h2>
      <input placeholder="Escribe una nota" />
    `
  }

  pintar()
  setInterval(pintar, 1000)
</script>

Y esta es la versión en React. El useEffect arranca el temporizador una vez y lo detiene al desmontar el componente, algo que estudiarás en el módulo 09:

import { useEffect, useState } from 'react'

export default function RelojReact() {
  const [ahora, setAhora] = useState(new Date())

  useEffect(() => {
    const id = setInterval(() => setAhora(new Date()), 1000)
    return () => clearInterval(id)
  }, [])

  return (
    <div style={{ padding: 20 }}>
      <h2>Son las {ahora.toLocaleTimeString('es-ES')}</h2>
      <input placeholder="Escribe una nota" />
    </div>
  )
}

Las dos parecen iguales: una plantilla que se repinta cada segundo con la hora nueva. He escrito «Llamar a Marta» en las dos cajas y he esperado poco más de un segundo:

Versión Texto antes Texto después Elemento con el foco después
innerHTML Llamar a Marta (vacío) BODY
React Llamar a Marta Llamar a Marta INPUT

Reloj hecho con innerHTML, con la caja de texto vacía tras repintar

Figura 4. Con innerHTML, cada segundo se destruye el input. Se pierden el texto y el foco.

Reloj hecho con React, con el texto Llamar a Marta intacto en la caja

Figura 5. Con React, solo cambia el nodo de texto de la hora. El input es siempre el mismo.

La diferencia está en que innerHTML crea nodos nuevos, mientras que React descubre que el input no ha cambiado y lo deja en paz. El texto escrito, el foco, la posición del cursor o una selección viven en el nodo real, así que se conservan. Con esto ves por qué React permite programar como si repintaras todo sin pagar lo que eso costaría.

Tres ideas falsas muy extendidas

«El DOM virtual hace que React sea más rápido que el DOM.» No puede serlo: al final React usa las mismas operaciones del DOM que tú, y antes hace trabajo extra para saber cuáles. Un código a mano bien afinado siempre puede igualarlo o superarlo. Lo que React aporta es un rendimiento suficiente en la gran mayoría de los casos, sin que tengas que afinar nada, y un código mucho más fácil de mantener.

«React vuelve a dibujar toda la página.» Vuelve a calcular el componente que cambió y sus hijos, pero solo toca en el DOM lo que difiere.

«Cada render es un problema de rendimiento que hay que evitar.» Ejecutar una función que devuelve objetos suele ser muy rápido. La medida anterior muestra un render sin ningún cambio en el DOM, y eso no es un error. El módulo 14 explica cuándo importa de verdad y cómo el React Compiler lo resuelve casi siempre sin esfuerzo.


5. Los conceptos básicos, de un vistazo

Antes de seguir, este es el vocabulario que oirás en todo el curso. Cada término tiene su módulo. Aquí basta con que los reconozcas.

Término Qué es Dónde se ve
Componente Función que devuelve una descripción de la interfaz. Su nombre empieza por mayúscula Módulo 03
JSX Sintaxis parecida a HTML dentro de JavaScript. Se transforma en llamadas a funciones Módulo 02
Props Los datos que un componente recibe de su padre. Son de solo lectura Módulo 03
Estado Datos propios de un componente, que React recuerda entre renders. Cambiarlos provoca un render Módulo 04
Hook Función que empieza por use y da a un componente acceso a capacidades de React, como useState Módulo 04
Evento Respuesta a una acción del usuario, como onClick u onChange Módulo 05
Efecto Código que sincroniza el componente con algo externo a React Módulo 09
Raíz El punto del DOM donde React toma el control, creado con createRoot Módulo 01

El flujo de datos en React va en una sola dirección, de padres a hijos mediante props. Si un hijo necesita comunicar algo hacia arriba, llama a una función que el padre le ha pasado. Esta restricción hace que siempre puedas averiguar de dónde viene un dato: subiendo por el árbol de componentes.

Prueba React ahora, sin instalar nada

Lo normal es trabajar con Vite, como harás desde el módulo 01. Pero React es solo JavaScript, y puedes verlo funcionar en un único fichero HTML. Guarda esto como primer-react.html y ábrelo con doble clic. Necesitas conexión a Internet.

<!doctype html>
<html lang="es">
  <head>
    <meta charset="utf-8" />
    <title>React sin instalar nada</title>
    <script type="importmap">
      {
        "imports": {
          "react": "https://esm.sh/react@19.3.1",
          "react-dom/client": "https://esm.sh/react-dom@19.3.1/client"
        }
      }
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module">
      import { createElement as h, useState } from 'react'
      import { createRoot } from 'react-dom/client'

      function Saludo() {
        const [nombre, setNombre] = useState('')
        return h('section', { style: { padding: 20, fontFamily: 'Segoe UI, Arial, sans-serif' } },
          h('h1', null, nombre ? `Hola, ${nombre}` : 'Escribe tu nombre'),
          h('input', {
            value: nombre,
            placeholder: 'Tu nombre',
            onChange: (e) => setNombre(e.target.value),
          }),
        )
      }

      createRoot(document.querySelector('#root')).render(h(Saludo))
    </script>
  </body>
</html>

Página con el título Hola, Marta y una caja de texto con Marta escrito

Figura 6. React funcionando en un solo fichero, descargado de un CDN.

Hay varias cosas interesantes en estas pocas líneas:

  • importmap indica al navegador de dónde sacar react cuando un módulo lo importe.
  • createElement, aquí renombrado como h, es la función con la que React crea sus descripciones. Recibe el tipo, las props y los hijos. JSX no es más que una forma cómoda de escribir estas llamadas, y por eso aquí no se usa: el navegador no entiende JSX sin un paso de compilación. El módulo 02 lo explica a fondo.
  • createRoot(...).render(...) es el punto de arranque: conecta React con un nodo del DOM.

⚠️ Importante: Este método sirve para experimentar, no para producción. Dependes de un servidor externo, no tienes TypeScript y envías más código del necesario. Desde React 19 ya no se publican las versiones UMD que antes se cargaban con <script src>, y por eso aquí se usa un CDN que sirve módulos ES, como recomienda la guía oficial de actualización.


6. Un poco de historia

Saber de dónde viene React explica por qué es como es, y te ayudará a entender el código antiguo que encontrarás en proyectos reales y en Internet.

Línea de tiempo de React desde 2013 hasta React 19.3 en 2026

Figura 7. Los hitos principales. Las fechas de las versiones son las de su publicación en npm.

2011-2013: el origen. Facebook tenía el problema descrito en la sección 2 a gran escala: interfaces con muchas actualizaciones cruzadas, como las notificaciones y los comentarios. Jordan Walke creó un prototipo inspirado en XHP, una extensión de PHP con la que Facebook escribía componentes en el servidor. La idea rompedora era tratar la interfaz como si se repintara por completo con cada cambio. Se publicó como código abierto en la JSConf US de mayo de 2013, y la recepción inicial fue fría: mezclar lo que parecía HTML con JavaScript se consideraba un error.

2015-2016: madurez. Nace React Native, se separa react-dom del núcleo y, en abril de 2016, la versión 0.14 da paso a la 15. Ese salto no refleja un gran cambio, sino que la API ya era estable y usarla en producción era lo habitual.

2017: Fiber y licencia MIT. React 16 reescribe el motor interno con una arquitectura llamada Fiber, que permite dividir el trabajo de render en unidades e interrumpirlo. La licencia pasa a MIT tras la polémica por la cláusula de patentes de la licencia anterior.

2019: los hooks. React 16.8 cambia la forma de programar. Hasta entonces, el estado requería componentes de clase, con métodos como componentDidMount y los problemas de this. Con los hooks, las funciones pueden tener estado y efectos, y la lógica se reutiliza sin envoltorios. Si ves class ... extends Component en un tutorial, está escrito con el estilo anterior. Las clases siguen funcionando, y solo son imprescindibles para los Error Boundaries que verás en el módulo 15.

2020-2022: concurrencia. React 17 (octubre de 2020) no añade funciones: prepara el camino para actualizar por partes. React 18 (marzo de 2022) introduce el renderizado concurrente, que permite marcar actualizaciones como no urgentes para que la interfaz siga respondiendo, junto con createRoot.

2024-2026: la serie 19. React 19 (diciembre de 2024) trae las Actions, use y los Server Components estables. En febrero de 2025, el equipo declara obsoleto Create React App, que durante años fue la forma estándar de empezar, y recomienda un framework o una herramienta como Vite. En octubre de 2025 llegan React Compiler 1.0 y el anuncio de la React Foundation, una entidad creada para dar a React un hogar neutral respecto a las empresas, con Amazon, Callstack, Expo, Meta, Microsoft, Software Mansion y Vercel como miembros fundadores. En diciembre de 2025 se publica una vulnerabilidad muy grave en Server Components, que se analiza en la sección 9.

💡 Consejo: Al buscar información sobre React, mira la fecha. Tutoriales con class, ReactDOM.render, propTypes, create-react-app o forwardRef son anteriores al React actual. La referencia fiable es la documentación oficial en react.dev.


7. React frente a Angular, Vue y Svelte

Te lo preguntarán en clase y en entrevistas. Lo útil no es decidir cuál es «mejor», sino entender en qué difieren. Estas son las versiones estables en npm el 2 de noviembre de 2026 y las descargas de la semana del 19 al 25 de octubre:

  React Angular Vue Svelte
Versión 19.3.1 22.2.4 3.5.44 5.57.3
Descargas semanales 126,1 millones 3,7 millones 11,6 millones 4,5 millones
Tipo Biblioteca Framework completo Framework progresivo Compilador y framework
Respaldo Meta y React Foundation Google Comunidad, con Evan You al frente Comunidad, con Rich Harris al frente
Plantillas JSX: JavaScript con marcado HTML con sintaxis propia HTML con directivas, en ficheros .vue HTML ampliado, en ficheros .svelte
TypeScript Opcional, aunque muy habitual Es el lenguaje de base Opcional, con buen soporte Opcional, con buen soporte
Rutas y formularios De terceros Incluidos Rutas y estado oficiales, aparte SvelteKit
Cómo detecta cambios Vuelve a ejecutar el componente y compara Signals que avisan a quien depende de ellos Objetos reactivos que registran quién los lee El compilador genera el código de actualización
Curva de aprendizaje Base pequeña, pero muchas decisiones Mucho que aprender al principio, pocas decisiones Suave Suave
Soporte de versiones Sin LTS Calendario publicado con LTS Sin calendario LTS equivalente Sin calendario LTS equivalente

⚠️ Importante: Las descargas de npm miden instalaciones, muchas de ellas automáticas en sistemas de integración continua o como dependencia de otros paquetes. Indican presencia en el ecosistema, no número de desarrolladores, y favorecen a React por el gran número de paquetes que dependen de él.

La diferencia técnica más profunda está en la fila de detección de cambios:

  • React no sabe qué ha cambiado dentro de tu componente. Cuando cambia el estado, ejecuta la función otra vez y compara el resultado. Es un modelo muy simple de razonar, porque todo tu componente es JavaScript normal que se ejecuta de arriba abajo. A cambio, se hace trabajo que a veces no era necesario, y de ahí la existencia de memo y del React Compiler.
  • Angular, Vue y Svelte usan reactividad de grano fino: el sistema sabe qué partes concretas de la interfaz dependen de cada dato y solo actualiza esas. Hay menos trabajo por defecto, a cambio de reglas propias sobre qué es reactivo y qué no.

Cuándo encaja cada uno

  • React es una apuesta segura si buscas empleo, necesitas un ecosistema enorme o quieres compartir conocimientos con el desarrollo móvil a través de React Native.
  • Angular encaja en equipos grandes que prefieren una estructura común ya decidida, y en entornos corporativos que valoran un calendario de soporte formal.
  • Vue ofrece una entrada muy suave y una buena integración en proyectos ya existentes.
  • Svelte produce poco código de ejecución y resulta muy agradable en proyectos pequeños y medianos.

Lo que aprendes es transferible. Componentes, estado, props, flujo de datos y rutas existen en todos ellos con otros nombres. Si dominas uno, el siguiente se aprende mucho más rápido.


8. El ecosistema React

Como React solo resuelve la interfaz, una aplicación real necesita otras piezas. Esta libertad es a la vez su mayor virtud y su mayor dificultad: hay que elegir.

React en el centro, rodeado de herramientas, rutas, datos, estado global, formularios, estilos, pruebas y frameworks

Figura 8. Las piezas de una aplicación React y los módulos del curso donde se estudian.

Estas son las elecciones de este curso, con las versiones con las que se ha comprobado cada ejemplo:

Necesidad Elección Versión Módulo
Herramienta de desarrollo y build Vite y @vitejs/plugin-react 8.3.5 y 6.1.2 01
Lenguaje TypeScript 7.0.2 13
Análisis de código oxlint 1.90 01
Rendimiento babel-plugin-react-compiler 1.0.0 14
Estilos Tailwind CSS 4.3.3 07
Rutas React Router 8.5.1 18
Datos del servidor TanStack Query 5.105.1 19
Estado global Zustand y Redux Toolkit 5.0.16 y 2.13.0 20
Formularios React Hook Form y Zod 7.91.0 y 4.6.7 21
Pruebas Vitest, Testing Library, MSW y Playwright 5.0.5, 16.3.3, 3.0.3 y 1.64.1 22
Framework completo Next.js 16.4.3 23

¿Biblioteca sola o framework?

Hay dos formas de arrancar un proyecto:

  • Vite + bibliotecas: creas una aplicación que se ejecuta en el navegador y añades lo que necesites. Tienes pleno control y menos conceptos. Es lo que hace este curso en casi todos los módulos, porque así se ve qué hace cada pieza.
  • Un framework como Next.js: trae las decisiones tomadas e incluye renderizado en el servidor, rutas por carpetas y Server Components. Es la opción habitual en sitios públicos donde importan el posicionamiento en buscadores y la velocidad de la primera carga.

La documentación oficial recomienda empezar con un framework en aplicaciones nuevas. Para aprender, empezar sin él tiene ventajas: un framework añade su propia capa de conceptos, y si no sabes qué aporta, no podrás valorarlo. Por eso Next.js llega en el módulo 23.

⚠️ Importante: Create React App está obsoleto desde febrero de 2025. Sigue apareciendo en tutoriales antiguos, pero no lo uses en proyectos nuevos. En su lugar, usa Vite, como verás en el módulo 01.


9. Versiones de React

Esta sección es larga porque el asunto da más problemas reales de los que parece. Una mala elección de versión puede dejar una aplicación con una vulnerabilidad conocida, o impedirte instalar una biblioteca.

Versionado semántico

React sigue el versionado semántico (semver). Un número como 19.3.1 tiene tres partes:

Parte Nombre Cuándo sube Ejemplo
19 Mayor (major) Cambios incompatibles De 18.3.1 a 19.0.0
3 Menor (minor) Funciones nuevas, compatibles con lo anterior De 19.2.0 a 19.3.0
1 Parche (patch) Errores críticos y vulnerabilidades De 19.3.0 a 19.3.1

La política oficial añade tres matices importantes:

  • Las versiones menores son las más frecuentes, y los parches se reservan para los errores más críticos y las vulnerabilidades.
  • No hay calendario fijo de versiones mayores. El equipo intenta que sean pocas y añade avisos en desarrollo antes de cada cambio. Según su política, si tu aplicación no muestra avisos en la última versión, será compatible con la siguiente mayor.
  • No cuentan como cambios incompatibles: los avisos de desarrollo, las API que empiezan por unstable_, las versiones canary y las API internas no documentadas.

Los rangos de package.json

En package.json no sueles escribir una versión exacta, sino un rango. npm elige la versión más alta que encaje. Esta es la salida real de comprobar varios rangos con semver, la biblioteca que usa npm:

^19.2.0   19.2.0                           true
^19.2.0   19.2.9                           true
^19.2.0   19.3.1                           true
^19.2.0   20.0.0                           false
^19.2.0   18.3.1                           false
~19.2.0   19.2.9                           true
~19.2.0   19.3.1                           false
19.2.x    19.2.9                           true
19.2.x    19.3.1                           false
19.2.7    19.2.7                           true
19.2.7    19.2.9                           false
^19.2.0   19.3.0-canary-e84e1492-20261031  false
>=19.2.7  19.2.6                           false
>=19.2.7  19.2.7                           true
>=19.2.7  19.3.1                           true
>=19.2.7  19.1.10                          false
Rango Significado Admite
^19.2.0 Misma mayor Menores y parches nuevos. Es el valor que pone npm por defecto
~19.2.0 Misma menor Solo parches nuevos
19.2.7 Exacta Solo esa versión
>=19.2.7 Desde esa versión Cualquier versión posterior, incluida una mayor futura

Fíjate en la línea de canary: las versiones preliminares no entran en un rango normal, aunque su número parezca encajar. No te llegará una por accidente.

💡 Consejo: package.json indica qué aceptas, y package-lock.json qué se instaló de verdad. Sube siempre ese segundo fichero al repositorio. Así, todo el equipo y tu servidor de integración usan las mismas versiones. El módulo 01 lo detalla.

Los canales de publicación

Las versiones de React no se publican por una sola vía. Hay tres canales, que en npm son etiquetas:

Tres canales que salen de la rama main: experimental, canary y latest, con su uso recomendado

Figura 9. Los canales de React, con los números que tenían el 2 de noviembre de 2026.

Esta es la salida real de npm view react dist-tags en esa fecha:

{
  "beta": "19.0.0-beta-26f2496093-20240514",
  "rc": "19.0.0-rc.1",
  "next": "19.3.0-canary-d5736f09-20260507",
  "latest": "19.3.1",
  "backport": "19.1.10",
  "experimental": "0.0.0-experimental-e84e1492-20261031",
  "canary": "19.3.0-canary-e84e1492-20261031"
}
Canal Qué es Sigue semver Para quién
latest Versiones estables. Es lo que instalas si no indicas nada Sí Cualquier aplicación. Es la recomendación oficial en caso de duda
canary La rama principal de desarrollo, candidata a la siguiente versión menor. Puede tener cambios incompatibles No Frameworks y entornos controlados, con versión fijada
experimental Funciones que aún no están listas No, ni hay garantías Para probar ideas. La política dice que no se use en aplicaciones de cara al usuario

El resto de etiquetas no son canales: beta y rc son restos de la preparación de React 19, y next lleva meses sin moverse. La etiqueta backport no aparece en la política de versiones. Apunta a 19.1.10, que fue el último parche publicado el 14 de octubre de 2026, once segundos después de 19.2.9. Todo indica que es una etiqueta auxiliar del proceso de publicación, para que un parche de una línea antigua no se convierta en latest. No debes elegirla como si fuera «la versión recomendada para mantenimiento».

Para instalar un canal concreto:

npm install react@latest react-dom@latest          # estable
npm install --save-exact react@canary react-dom@canary   # canary, con versión fijada

📘 Concepto: Un detalle que sorprende: Next.js incluye su propia copia de React. Dentro del paquete next 16.4.3, en dist/compiled/react, hay una copia de 19.3.0-canary-278794d7-20261002, aunque tu package.json diga otra cosa. Es el uso previsto del canal canary: un framework fija una versión y responde por ella. Lo verás en el módulo 23.

React no tiene LTS

Muchos proyectos ofrecen versiones LTS (Long-Term Support, soporte a largo plazo): versiones concretas con una fecha de fin de soporte anunciada, a las que se les garantizan correcciones durante años. Node.js y Angular funcionan así.

React no tiene LTS. La página oficial de versiones no define ningún programa de soporte a largo plazo, ni ventanas de soporte, ni fechas de fin de vida. Lo único que promete es esto, en sus propias palabras:

If we learn of a security vulnerability in React, we release a backported fix for all major versions that are affected by the vulnerability.

Es decir: si descubren una vulnerabilidad, publican la corrección para todas las versiones mayores afectadas. No hay más compromiso, y esto tiene varias consecuencias:

  • Nadie te dice hasta cuándo se mantendrá una versión. No hay fecha que apuntar en un plan de proyecto.
  • Solo se promete corregir errores de seguridad. Los errores normales se corrigen, en principio, en la versión actual.
  • Los parches llegan solo si la vulnerabilidad afecta a tu versión. Una versión antigua sin parches recientes no es necesariamente insegura, pero tampoco tienes ninguna garantía sobre ella.

En la práctica, el equipo ha ido más allá de lo prometido. Desde diciembre de 2025 viene publicando parches a la vez en cada línea menor de la serie 19:

Tabla con ocho tandas de parches publicadas a la vez en las líneas 19.0, 19.1 y 19.2, y ninguna en la 18.3

Figura 10. Tandas de parches según las fechas de publicación de npm.

De esta tabla se sacan dos lecciones:

  • Las líneas 19.0, 19.1 y 19.2 reciben parches hoy, y por eso las versiones 19.0.9, 19.1.10, 19.2.9 y 19.3.1 están todas al día. Es un buen hábito del equipo, pero no un compromiso por escrito. No lo planifiques como si lo fuera.
  • La línea 18 no ha recibido nada desde abril de 2024. La vulnerabilidad de diciembre de 2025 no le afectaba, así que la política no exigía corregirla. Pero 18.3.1 lleva más de dos años sin ningún cambio.
  Node.js Angular React
Versiones LTS con nombre Sí Sí No
Fechas de fin de soporte publicadas Sí Sí No
Promesa escrita sobre versiones antiguas Ventana definida para cada versión Ventana definida para cada versión Solo seguridad, cuando la vulnerabilidad les afecte

Por eso en este curso no hay una versión de referencia LTS. Lo que importa es la versión que tengas tú, y cada novedad se marca con la versión exacta en la que apareció, como se explica en la sección 12.

La lección de diciembre de 2025

El 3 de diciembre de 2025, el equipo de React publicó una vulnerabilidad con la máxima gravedad posible: CVE-2025-55182, con una puntuación CVSS de 10,0. Permitía ejecutar código de forma remota en el servidor, sin autenticación.

Dato Valor
Afecta a Los paquetes react-server-dom-webpack, react-server-dom-parcel y react-server-dom-turbopack
Versiones afectadas 19.0, 19.1.0, 19.1.1 y 19.2.0
Versiones corregidas 19.0.1, 19.1.2 y 19.2.1
Quién queda fuera Las aplicaciones sin servidor React, y las que no usan un framework o empaquetador compatible con Server Components
Vulnerabilidades relacionadas posteriores CVE-2025-55184, CVE-2025-67779, CVE-2025-55183 y CVE-2026-23864

Una aplicación que solo se ejecuta en el navegador, como las de casi todo este curso, no estaba afectada. Pero quien usara Next.js u otro framework con Server Components tuvo que actualizar de inmediato, y además varias veces en pocas semanas, porque la investigación destapó más fallos.

De aquí sale una regla práctica: fijar una versión no es lo mismo que quedarse quieto. Si fijas 19.2.0 para siempre, los parches no te llegan. Conviene mantenerse en la última versión de tu línea y actualizar con frecuencia. El caso se analiza con detalle en el módulo 23.

react y react-dom van juntos

Los dos paquetes deben tener exactamente la misma versión. No basta con que la versión sea parecida. react-dom 19.3.1 declara react: ^19.3.1 como dependencia par, y además lo comprueba al arrancar. Esto ocurre si los mezclas:

Error: Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:
  - react:      19.2.9
  - react-dom:  19.3.1
Learn more: https://react.dev/warnings/version-mismatch

No es un simple aviso: es un error que detiene la aplicación, y salta igual en desarrollo y en producción. Lo he provocado con el renderizador de servidor de react-dom.

Los tipos de TypeScript, @types/react y @types/react-dom, se publican aparte y su número no coincide siempre con el de React: en este curso usas React 19.3.1 con tipos 19.3.0. Lo que debe coincidir es la mayor y la menor.

💡 Consejo: Actualiza siempre los dos en la misma orden: npm install react@19.3.1 react-dom@19.3.1.

Lo que piden las bibliotecas

Tu versión de React también está condicionada por las bibliotecas que uses. Cada una declara qué React acepta. Estos son los valores reales de las versiones de este curso:

Biblioteca Versión React que acepta Node que pide
react-router 8.5.1 >=19.2.7 >=22.22.0
@tanstack/react-query 5.105.1 ^18 \|\| ^19 Sin indicar
zustand 5.0.16 >=18.0.0 >=12.20.0
react-redux 9.3.1 ^18.0 \|\| ^19 Sin indicar
@reduxjs/toolkit 2.13.0 ^16.9.0 \|\| ^17.0.0 \|\| ^18 \|\| ^19 Sin indicar
react-hook-form 7.91.0 ^16.8.0 \|\| ^17 \|\| ^18 \|\| ^19 >=18.0.0
@testing-library/react 16.3.3 ^18.0.0 \|\| ^19.0.0 >=18
motion 14.9.0 ^18.0.0 \|\| ^19.0.0 Sin indicar
next 16.4.3 ^18.2.0 \|\| ^19.0.0, además de una versión preliminar >=20.9.0
@vitejs/plugin-react 6.1.2 Sin indicar ^20.19.0 \|\| >=22.12.0

Casi todas aceptan React 18 y 19. React Router 8 es la excepción, y es la que más condiciona: pide React 19.2.7 o superior. Si intentas instalarlo con React 19.1, npm se detiene. Esta es la salida real:

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: x@1.0.0
npm error Found: react@19.1.10
npm error node_modules/react
npm error   react@"19.1" from the root project
npm error
npm error Could not resolve dependency:
npm error peer react@">=19.2.7" from react-router@8.5.1
npm error node_modules/react-router
npm error   react-router@"8" from the root project

⚠️ Importante: Evita resolver este error con --force o --legacy-peer-deps. Esas opciones no arreglan la incompatibilidad, solo apagan el aviso, y el fallo puede aparecer más tarde, en ejecución. Lo correcto es actualizar React o usar una versión de la biblioteca que admita el tuyo.


10. ¿Qué versión elegir?

Con todo lo anterior, la decisión es bastante mecánica:

Árbol de decisión para elegir versión de React según proyecto nuevo, framework, serie 19 o versión antigua

Figura 11. Cómo elegir versión de React.

Proyecto nuevo: la última estable

Si empiezas desde cero, o sigues este curso, usa React 19.3.1 o la última estable que haya cuando lo leas. Tendrás todo lo que explica el curso, y es la versión que recibe más atención. No hay ninguna LTS más «segura» que elegir.

Uso de un framework: la que diga el framework

Con Next.js o Expo, la versión la determina el framework. Sigue sus instrucciones de actualización en lugar de cambiar React por tu cuenta.

Aplicación en la serie 19: último parche, y sube cuando puedas

Si tu aplicación está, por ejemplo, en 19.1, ve como mínimo a 19.1.10 en cuanto puedas. Un parche no debería romper nada y puede traer correcciones de seguridad. Después, sube de versión menor cuando tengas ocasión: según semver, no debería haber cambios incompatibles, aunque hay cambios de comportamiento que conviene leer en las notas de cada versión.

Para quedarte en una línea, indica solo mayor y menor:

npm install react@19.2 react-dom@19.2

Esa orden instala hoy 19.2.9, el último parche de la línea. Con TypeScript, ajusta también los tipos:

npm install -D @types/react@19.2 @types/react-dom@19.2

Aplicación en React 18 o anterior: planifica la migración

React 18 sigue funcionando, pero no recibe nada desde abril de 2024, y varias bibliotecas ya no lo aceptan. La ruta recomendada por la guía oficial de actualización es:

  1. Sube a 18.3.1. Es idéntica a 18.2, pero añade avisos para todo lo que desaparecerá en la 19. Por ejemplo, en su código de desarrollo está el mensaje Support for defaultProps will be removed from function components.
  2. Corrige todos los avisos que veas en la consola.
  3. Instala React 19 junto con sus tipos.
  4. Ejecuta las recetas automáticas:
npx codemod@latest react/19/migration-recipe
npx types-react-codemod@latest preset-19 ./ruta-de-tu-app

La lista de lo que desaparece está en la sección 11.

¿Qué pierdes en este curso con cada versión?

Si no puedes usar la última versión, esta tabla indica desde qué versión funciona cada parte del temario. Los apartados afectados llevan su marca dentro de cada módulo.

Tu versión Lo que no tendrás
19.3 Nada: es la versión del curso
19.2 <ViewTransition>, addTransitionType, browser(), Fragment Refs y Trusted Types (módulos 10 y 17), <Context> directo en Server Components (módulo 23)
19.1 Además: <Activity>, useEffectEvent, cacheSignal y Performance Tracks (módulos 09, 14 y 17). Tampoco React Router 8, que pide 19.2.7 (módulos 18 y 26)
19.0 Además: captureOwnerStack (módulo 15)
18.3 Además: ref como prop, <Context> como proveedor, use, limpieza de refs, metadatos y hojas de estilo en componentes, <form action> y todo el módulo de Actions (módulos 03, 07, 08, 10, 11, 15 y 16)

Cómo saber qué versión tienes

Hay tres formas, cada una con su utilidad:

1. En la terminal, en la carpeta del proyecto. Es lo que hay realmente instalado:

npm ls react react-dom

2. En package.json ves el rango aceptado, no la versión instalada. ^19.2.0 no dice qué tienes exactamente.

3. En el código, con la constante version, que indica lo que se está ejecutando de verdad:

import { version } from 'react'

console.log(version) // 19.3.1

Comprobar funciones, no solo números

Este componente muestra tu versión y comprueba qué exportaciones tienes. Mira si cada nombre existe en el módulo, en lugar de comparar números:

import * as React from 'react'
import * as ReactDOM from 'react-dom'

// Cada fila: qué exportación buscamos, en qué paquete y desde qué versión existe
const FUNCIONES = [
  { nombre: 'use', paquete: React, desde: '19.0' },
  { nombre: 'useActionState', paquete: React, desde: '19.0' },
  { nombre: 'useOptimistic', paquete: React, desde: '19.0' },
  { nombre: 'captureOwnerStack', paquete: React, desde: '19.1' },
  { nombre: 'Activity', paquete: React, desde: '19.2' },
  { nombre: 'useEffectEvent', paquete: React, desde: '19.2' },
  { nombre: 'ViewTransition', paquete: React, desde: '19.3' },
  { nombre: 'addTransitionType', paquete: React, desde: '19.3' },
  { nombre: 'browser', paquete: ReactDOM, desde: '19.3' },
]

export default function PanelVersion() {
  return (
    <main style={{ padding: 20, maxWidth: 560 }}>
      <h1>Tienes React {React.version}</h1>
      <table style={{ width: '100%', borderCollapse: 'collapse' }}>
        <thead>
          <tr style={{ textAlign: 'left', background: '#e8eaf6' }}>
            <th>Función</th>
            <th>Desde</th>
            <th>¿Disponible?</th>
          </tr>
        </thead>
        <tbody>
          {FUNCIONES.map(({ nombre, paquete, desde }) => {
            const existe = nombre in paquete
            return (
              <tr key={nombre}>
                <td><code>{nombre}</code></td>
                <td>{desde}</td>
                <td style={{ color: existe ? '#2e7d32' : '#d84315' }}>
                  {existe ? 'Sí' : 'No'}
                </td>
              </tr>
            )
          })}
        </tbody>
      </table>
    </main>
  )
}

He ejecutado exactamente este componente con las dos versiones, sin cambiar nada:

Panel en React 19.3.1 con todas las funciones disponibles

Figura 12. Con React 19.3.1, todo está disponible.

Panel en React 18.3.1 con todas las funciones marcadas como no disponibles

Figura 13. El mismo código con React 18.3.1. Ninguna de estas funciones existe.

⚠️ Importante: Este panel debe ejecutarse en desarrollo. captureOwnerStack solo existe en la compilación de desarrollo de React: en producción, el panel diría «No» incluso en 19.3. Lo he comprobado listando las exportaciones de cada versión en ambos modos. Es una función de depuración, y no debes llamarla sin comprobar antes que exista.


11. Novedades de la serie 19

Primero, el resumen. La columna «Exportación» indica cómo se accede a cada función, y las de react y react-dom se han comprobado una a una comparando los paquetes de cada versión.

Versión Fecha Novedad Exportación Módulo
19.0 Dic. 2024 Actions y <form action> useActionState, useOptimistic, useFormStatus, requestFormReset 08, 16
19.0   Leer promesas y contexto en el render use 11, 15
19.0   ref como prop normal y limpieza de refs No son exportaciones 03, 10
19.0   <Context> como proveedor No es una exportación 11
19.0   Metadatos y hojas de estilo en componentes <title>, <meta>, <link> 07
19.0   Server Components y Server Functions cache 23
19.0   Precarga de recursos preload, preinit, preconnect, prefetchDNS 14
19.1 Mar. 2025 Owner Stacks captureOwnerStack 15
19.2 Oct. 2025 Ocultar sin desmontar Activity 17
19.2   Eventos dentro de efectos useEffectEvent 09
19.2   Fin de la vida de la caché cacheSignal 17
19.2   Performance Tracks en Chrome Herramienta 14
19.3 Sep. 2026 Animaciones entre estados ViewTransition, addTransitionType 17
19.3   Agrupar nodos con una ref <Fragment ref> 10
19.3   Excluir del renderizado en servidor browser en react-dom 17
19.3   Trusted Types No es una exportación 17

Las exportaciones públicas han crecido así en la compilación de desarrollo:

18.3.1 react 35 react-dom 11
19.0.9 react 38 react-dom 13
  + cache unstable_useCacheRefresh use useActionState useOptimistic
  - createFactory unstable_act
  dom + preconnect prefetchDNS preinit preinitModule preload preloadModule requestFormReset useFormState useFormStatus
  dom - createRoot findDOMNode hydrate hydrateRoot render unmountComponentAtNode unstable_renderSubtreeIntoContainer
19.1.10 react 39 react-dom 13
  + captureOwnerStack
19.2.9 react 42 react-dom 13
  + Activity cacheSignal useEffectEvent
19.3.1 react 44 react-dom 14
  + ViewTransition addTransitionType
  dom + browser

Observa que createRoot «desaparece» de react-dom en 19.0. No se ha eliminado: desde React 18 se importa desde react-dom/client, y lo que se ha retirado es la entrada antigua en react-dom.

React 19.0 🆕 19.0

🆕 Desde React 19.0. No existe en React 18.3 ni anteriores. Si usas una versión anterior: gestiona el envío de formularios con useState para el estado pendiente y los errores, usa forwardRef para pasar refs, <Contexto.Provider> como proveedor, y useContext en lugar de use. Los Server Components no tienen alternativa.

El tema central son las Actions: funciones asíncronas en una transición, con las que React gestiona por ti el estado pendiente, los errores y las actualizaciones optimistas. También simplifica piezas que eran engorrosas: ref pasa a ser una prop normal, así que forwardRef ya no es necesario; <Contexto> sustituye a <Contexto.Provider>; y <title>, <meta> y <link> se pueden escribir en cualquier componente, y React los lleva al <head>.

Lo que eliminó React 19.0 🔄 19.0

🔄 Cambia en React 19.0. En React 18.3 y anteriores estas API siguen disponibles, con avisos en 18.3. Si usas una versión anterior: tu código funciona, pero debes sustituirlas antes de actualizar: sube a 18.3.1, corrige los avisos y aplica las recetas automáticas de migración.

Eliminado Sustituto
ReactDOM.render, ReactDOM.hydrate createRoot, hydrateRoot
unmountComponentAtNode root.unmount()
findDOMNode Refs
propTypes y defaultProps en funciones TypeScript y valores por defecto de ES6
String refs Refs con función o useRef
Context heredado (contextTypes) createContext
React.createFactory JSX
react-test-renderer/shallow El paquete react-shallow-renderer
react-dom/test-utils act se importa desde react
Versiones UMD Módulos ES desde un CDN
useRef() sin argumento, en los tipos useRef(null) o un valor inicial

React 19.1 🆕 19.1

🆕 Desde React 19.1. No existe en React 19.0 ni anteriores. Si usas una versión anterior: usa la pila de componentes errorInfo.componentStack que reciben los Error Boundaries, y las herramientas de desarrollo. Pierdes poder pedir esa pila en cualquier punto del código.

Es una versión centrada en la depuración. captureOwnerStack indica qué componentes crearon el que está fallando, lo que aclara mucho los mensajes de error.

React 19.2 🆕 19.2

🆕 Desde React 19.2. No existe en React 19.1 ni anteriores. Si usas una versión anterior: en lugar de <Activity>, renderiza de forma condicional, perdiendo el estado, u oculta con CSS, sin parar los efectos. En lugar de useEffectEvent, guarda el último valor en una ref. cacheSignal y Performance Tracks no tienen alternativa directa.

<Activity> oculta una parte de la interfaz sin perder su estado, parando sus efectos mientras no se ve. useEffectEvent resuelve un problema antiguo: leer el valor más reciente dentro de un efecto sin que ese valor lo reinicie. cacheSignal, solo para Server Components, permite cancelar trabajo cuando deja de ser útil. Además, eslint-plugin-react-hooks pasa a la configuración plana, y el prefijo de useId cambia de «r» a _r_, para que los identificadores sean válidos en más contextos.

React 19.3 🆕 19.3

🆕 Desde React 19.3. No existe en React 19.2 ni anteriores. Si usas una versión anterior: para animar, usa document.startViewTransition con flushSync, o la biblioteca Motion. En lugar de <Fragment ref>, añade un elemento contenedor. En lugar de browser(), muestra el contenido en el cliente tras montar el componente.

Con <ViewTransition> animas elementos al entrar, salir, actualizarse o compartir nombre entre dos posiciones, siempre que la actualización sea una transición, y solo en el DOM. addTransitionType indica el motivo de la transición, para animar de forma distinta al avanzar o retroceder. Las Fragment Refs permiten tratar como grupo los hijos de un <Fragment>, sin añadir un div. browser() excluye un componente del renderizado en servidor. Con Trusted Types, React deja de convertir a texto los valores antes de pasarlos al DOM.

Cambios de comportamiento de React 19.3 🔄 19.3

🔄 Cambia en React 19.3. En React 19.2 y anteriores una transición lenta puede retener a las demás, y Strict Mode no duplica los efectos durante la hidratación. Si usas una versión anterior: no necesitas hacer nada, pero al actualizar revisa el código que dependía de que varias transiciones terminaran juntas, y los efectos que no toleren ejecutarse dos veces.

  • Las transiciones se renderizan de forma independiente: una lenta ya no retiene a otras.
  • Strict Mode invoca dos veces los efectos también durante la hidratación, igual que en las raíces creadas en el cliente.
  • Hay un aviso en desarrollo si use se usa mal dentro de un condicional.
  • Un Server Component puede renderizar <Context> directamente.
  • Hay nuevos eventos onFullscreenChange y onFullscreenError, y el evento submit incluye submitter.
  • onReset se dispara cuando React reinicia un formulario tras una Server Action.

React Compiler 1.0

No es una versión de React, sino una herramienta aparte, y por eso no lleva marca. Se publicó como estable en octubre de 2025. Analiza tus componentes durante la compilación y añade por ti la memoización que antes se escribía a mano con useMemo y useCallback. El anuncio oficial indica que es compatible con React 17 y posteriores: en 17 y 18 hay que configurar target e instalar además react-compiler-runtime. Lo verás en el módulo 14.


12. Las marcas de versión de este curso

El curso se ha escrito y probado con React 19.3.1, pero puedes seguirlo con otra versión si es la que usas en tu centro o en tu trabajo. Para eso, cada apartado que dependa de una versión lleva una marca al final de su encabezado:

Marca Significado Ejemplo de encabezado
🆕 19.x La función no existía antes de esa versión ### Activity 🆕 19.2
🔄 19.x La función existía, pero cambia su comportamiento ### Transiciones independientes 🔄 19.3

Justo debajo del encabezado aparece siempre una caja con una frase fija, «Si usas una versión anterior», que indica qué hacer en su lugar. Por ejemplo:

### Activity 🆕 19.2

> **🆕 Desde React 19.2.** No existe en React 19.1 ni anteriores. **Si usas una versión anterior:** renderiza de forma condicional, sabiendo que perderás el estado, u oculta con CSS, sin parar los efectos.

Si un apartado no tiene marca, funciona igual desde React 18.3. Como React no tiene LTS, las marcas no comparan con una versión oficial, sino que indican la versión exacta. Así, cada persona puede comparar con la suya: si tienes la 19.1, todo lo marcado 🆕 19.2 y 🆕 19.3 no te funcionará, y lo marcado 🆕 19.0 y 🆕 19.1 sí.

💡 Consejo: Si estás aprendiendo y no tienes ninguna restricción, ignora las marcas: usa la última versión. Te serán útiles cuando trabajes en un proyecto real con una versión más antigua.


13. Mapa del curso

El curso tiene 27 módulos, organizados en seis etapas:

Mapa del curso con seis etapas: fundamentos, interactividad, React a fondo, React moderno, ecosistema y producción

Figura 14. Las seis etapas del curso.

Etapa Módulos Qué sabrás hacer al terminarla
1. Fundamentos 00 a 03 Crear un proyecto y escribir componentes con JSX y props
2. Interactividad 04 a 08 Hacer interfaces con estado, listas, estilos y formularios
3. React a fondo 09 a 14 Sincronizar con sistemas externos, organizar la lógica en hooks, tipar con TypeScript y optimizar
4. React moderno 15 a 17 Usar Suspense, concurrencia, Actions y animaciones
5. Ecosistema 18 a 22 Añadir rutas, datos del servidor, estado global, formularios avanzados y pruebas
6. Producción 23 a 26 Renderizar en servidor, hacerlo accesible, desplegar y completar el proyecto final

Qué necesitas saber antes de empezar

  • HTML y CSS: etiquetas, formularios, selectores, Flexbox. Repásalo en el Curso de HTML y CSS.
  • JavaScript moderno: funciones flecha, desestructuración, spread, map, filter, módulos, promesas y async/await. Repásalo en el Curso de JavaScript.
  • TypeScript: no hace falta conocerlo. Los ejemplos lo usan desde el módulo 02 de forma gradual, y se explica cada cosa cuando aparece. El módulo 13 lo trata a fondo.
  • Node.js: te hará falta tenerlo instalado, en versión 22.22.0 o superior para que funcione React Router 8. El módulo 01 te guía.

La aplicación EMPRESA

Todos los módulos usan los datos de una empresa ficticia: empleados, departamentos, productos, clientes y pedidos. Son los mismos datos de los cursos de Bases de Datos y Java Avanzado. Los ejemplos de cada módulo son independientes, así que puedes leerlos en cualquier orden. El proyecto final los reúne en una aplicación completa.

Para los módulos que trabajan con un servidor hay una API de ejemplo que se ejecuta en tu equipo. No tiene dependencias y solo necesita Node 22 o superior. Descarga empresa-api.zip, descomprímelo, entra en la carpeta empresa-api y arráncala:

node server.mjs

La API queda en http://localhost:3000/api. Las lecturas son públicas, y puedes probarla desde otra terminal. He dado formato a la respuesta para que se lea mejor:

curl "http://localhost:3000/api/empleados?tamano=1"
{
  "contenido": [
    {
      "empId": 100,
      "nombre": "Ana",
      "apellidos": "García López",
      "email": "ana.garcia@empresa.es",
      "telefono": "600100100",
      "fechaAlta": "2010-01-15",
      "oficio": "DIRECTOR GENERAL",
      "salario": 6500,
      "comision": null,
      "jefeId": null,
      "deptId": 10,
      "departamento": "DIRECCIÓN"
    }
  ],
  "pagina": 1,
  "tamano": 1,
  "total": 24,
  "paginas": 24
}

Las listas devuelven una página de resultados en contenido, junto con los datos de paginación. Si quieres ver bien los estados de carga, arráncala con la variable RETARDO, que añade una espera en milisegundos a cada respuesta. Los datos están en memoria: al reiniciar, vuelven a su estado inicial.

Cómo sacar partido al curso

  • Escribe el código. Leer un ejemplo no es lo mismo que hacerlo funcionar. Los errores que cometas al teclear enseñan más que los ejemplos correctos.
  • Rompe los ejemplos a propósito. Cambia algo y anticipa qué pasará antes de ejecutarlo.
  • Haz los ejercicios antes de mirar la solución. Cada módulo tiene entre 8 y 12, y al menos la mitad están resueltos.
  • Usa las React DevTools desde el módulo 01. Ver el árbol de componentes y su estado aclara mucho.

14. Ejercicios

Ejercicio 1: Imperativo o declarativo

Clasifica cada fragmento como imperativo o declarativo y explica por qué.

// A
elemento.classList.add('activo')

// B
let suma = 0
for (let i = 0; i < precios.length; i++) suma += precios[i]

// C
const baratos = productos.filter((p) => p.precio < 10)
/* D */
.boton:hover { background: #3f51b5; }
-- E
SELECT nombre FROM empleados WHERE salario > 3000;
// F
<li className={activo ? 'activo' : ''}>{nombre}</li>
Ver solución
Fragmento Tipo Por qué
A Imperativo Es una orden sobre el DOM: añade esta clase ahora
B Imperativo Describes el recorrido, el índice y el acumulador paso a paso
C Declarativo Dices qué condición deben cumplir. El recorrido lo hace filter
D Declarativo Describes cómo debe verse en un estado. Es el navegador quien cambia el color y lo revierte
E Declarativo Pides un resultado. El gestor decide cómo obtenerlo
F Declarativo Describes la clase para cada valor de activo, sin ordenar un cambio

Compara A y F, porque es la diferencia fundamental de este módulo. A cambia algo en un momento dado, y debes acordarte de ejecutar la operación contraria cuando deje de estar activo. F es siempre cierto: para cualquier valor de activo, indica qué clase corresponde.

La frontera no es absoluta, ya que C es imperativo visto desde dentro de filter. Lo declarativo es una cuestión de nivel: alguien escribe los pasos, pero tú no.

Ejercicio 2: Contador en los dos estilos

Crea un contador con dos botones, «Sumar» y «Reiniciar», y un texto «Clics: N». El botón «Reiniciar» debe estar deshabilitado cuando la cuenta sea 0. Escríbelo primero con DOM a mano y después en React. ¿Cuántos sitios del código debes tocar para que el botón se comporte bien en cada versión?

Ver solución

Con DOM a mano:

<div style="padding: 20px">
  <p>Clics: <output id="cuenta">0</output></p>
  <button id="sumar">Sumar</button>
  <button id="reiniciar" disabled>Reiniciar</button>
</div>
<script>
  const salida = document.querySelector('#cuenta')
  const sumar = document.querySelector('#sumar')
  const reiniciar = document.querySelector('#reiniciar')
  let cuenta = 0

  sumar.addEventListener('click', () => {
    cuenta += 1
    salida.textContent = cuenta
    reiniciar.disabled = false
  })

  reiniciar.addEventListener('click', () => {
    cuenta = 0
    salida.textContent = cuenta
    reiniciar.disabled = true
  })
</script>

En React:

import { useState } from 'react'

export default function ContadorReact() {
  const [cuenta, setCuenta] = useState(0)

  return (
    <div style={{ padding: 20 }}>
      <p>Clics: <output>{cuenta}</output></p>
      <button onClick={() => setCuenta(cuenta + 1)}>Sumar</button>
      <button onClick={() => setCuenta(0)} disabled={cuenta === 0}>Reiniciar</button>
    </div>
  )
}

He probado las dos con el mismo guion: al empezar, «Reiniciar» está deshabilitado; tras dos clics en «Sumar», la cuenta vale 2 y el botón se habilita; tras reiniciar, vuelve a 0 y a deshabilitarse. Las dos dan el mismo resultado.

La diferencia está en el botón. A mano, su estado aparece en tres sitios: el disabled del HTML inicial y una línea en cada manejador. Si mañana añades un botón «Restar», tendrás que acordarte de escribir una cuarta. En React hay uno solo: disabled={cuenta === 0}. Es una regla que se cumple siempre, sea cual sea la forma en que se haya llegado a ese valor.

Ejercicio 3: Espía el DOM

Crea un componente con un título, un texto fijo, un contador y un botón. Usa MutationObserver para mostrar en la consola cada cambio del DOM. ¿Cuántas mutaciones hay con cada clic? ¿Afectan al título?

Ver solución
import { useEffect, useState } from 'react'

export default function ContadorEspiado() {
  const [cuenta, setCuenta] = useState(0)

  useEffect(() => {
    const observador = new MutationObserver((cambios) => {
      for (const cambio of cambios) {
        console.log(cambio.type, cambio.target.nodeName, cambio.target.textContent)
      }
    })
    observador.observe(document.querySelector('#root')!, {
      subtree: true,
      childList: true,
      characterData: true,
      attributes: true,
    })
    return () => observador.disconnect()
  }, [])

  return (
    <section style={{ padding: 20 }}>
      <h2>Panel de control</h2>
      <p>Texto que nunca cambia.</p>
      <p>Clics: <strong>{cuenta}</strong></p>
      <button onClick={() => setCuenta(cuenta + 1)}>Sumar</button>
    </section>
  )
}

Esta es la salida real tras dos clics:

characterData #text 1
characterData #text 2

Cada clic produce una sola mutación: cambiar el valor de un nodo de texto. Ni el título, ni el párrafo, ni el strong, ni el botón se tocan, aunque el componente completo se haya vuelto a ejecutar. Es la fase de commit de la figura 3 en acción.

El useEffect se usa aquí para crear el observador una vez y desconectarlo al terminar. Lo estudiarás en el módulo 09.

Ejercicio 4: Lee los rangos

Sin ejecutar nada, indica qué versión instalará npm para cada rango, si las versiones publicadas fueran 18.3.1, 19.2.0, 19.2.9, 19.3.0 y 19.3.1:

  • a) ^19.2.0
  • b) ~19.2.0
  • c) ^18.0.0

Explica además por qué ^19.2.0 no instalaría nunca React 20, y qué ocurre con >=19.2.7, el rango que usa React Router.

Ver solución

Esta es la salida real de semver.maxSatisfying con esa lista:

^19.2.0 -> 19.3.1
~19.2.0 -> 19.2.9
^18.0.0 -> 18.3.1
  • ^ permite subir menor y parche, pero no la mayor. Por eso ^19.2.0 no llega a React 20, y ^18.0.0 se queda en la última de la 18.
  • ~ solo permite subir el parche, y por eso se queda en 19.2.9.
  • >=19.2.7 no tiene techo: admite una futura versión 20. Es un rango más arriesgado, porque declara compatibilidad con algo que aún no existe. Si aparece una 20 con cambios incompatibles, el rango seguirá aceptándola.

Ejercicio 5: Compara versiones de verdad

Escribe en TypeScript una función cumpleVersion(actual, minima) que indique si una versión es igual o posterior a otra, ignorando sufijos como -canary-.... Después, crea un componente que avise si tu React sirve para React Router 8. Comprueba que 19.10.0 es posterior a 19.2.7. ¿Por qué no funciona comparar directamente dos textos con >=?

Ver solución
/** Convierte '19.2.7' en [19, 2, 7]. Ignora sufijos como '-canary-e84e1492-20261031'. */
function partes(version: string): number[] {
  return version.split('-')[0].split('.').map(Number)
}

/** Indica si `actual` es igual o posterior a `minima`. */
export function cumpleVersion(actual: string, minima: string): boolean {
  const a = partes(actual)
  const m = partes(minima)
  for (let i = 0; i < 3; i++) {
    if (a[i] !== m[i]) return a[i] > m[i]
  }
  return true
}
import { version } from 'react'
import { cumpleVersion } from './cumpleVersion'

const MINIMA_ROUTER = '19.2.7'

export default function AvisoVersion() {
  const valida = cumpleVersion(version, MINIMA_ROUTER)
  return (
    <p style={{ padding: 20 }}>
      {valida
        ? `React ${version} cumple lo que pide React Router 8 (${MINIMA_ROUTER}).`
        : `React ${version} es demasiado antiguo: React Router 8 pide ${MINIMA_ROUTER}.`}
    </p>
  )
}

Salida real de la función con varios casos:

19.3.1                           19.2.7   true
19.2.7                           19.2.7   true
19.2.6                           19.2.7   false
19.1.10                          19.1.9   true
19.10.0                          19.2.7   true
18.3.1                           19.2.7   false
20.0.0                           19.2.7   true
19.3.0-canary-e84e1492-20261031  19.2.7   true
Comparando texto: false

La última línea, resultado de '19.10.0' >= '19.2.7', es la clave. Los textos se comparan carácter a carácter: en '19.10.0' y '19.2.7', los primeros tres caracteres coinciden, y luego '1' es menor que '2'. El texto dice que 19.10 es anterior a 19.2, cuando es al revés. Por eso hay que convertir cada parte en número.

El componente, ejecutado de verdad, muestra con cada versión:

React 19.3.1 cumple lo que pide React Router 8 (19.2.7).
React 18.3.1 es demasiado antiguo: React Router 8 pide 19.2.7.

En un proyecto real usarías el paquete semver, que resuelve también los casos de versiones preliminares. Esta función ignora el sufijo, así que trata una canary como si fuera la versión final.

Ejercicio 6: Panel resumido

Adapta el panel de la sección 10: comprueba use, Activity y ViewTransition de react, además de useFormStatus y requestFormReset de react-dom. En lugar de una tabla, muestra un resumen con el número de funciones disponibles y, solo si falta alguna, cuáles son.

Ver solución
import * as React from 'react'
import * as ReactDOM from 'react-dom'

const FUNCIONES = [
  { nombre: 'use', paquete: React },
  { nombre: 'Activity', paquete: React },
  { nombre: 'ViewTransition', paquete: React },
  { nombre: 'useFormStatus', paquete: ReactDOM },
  { nombre: 'requestFormReset', paquete: ReactDOM },
]

export default function PanelResumen() {
  const disponibles = FUNCIONES.filter(({ nombre, paquete }) => nombre in paquete)
  const faltan = FUNCIONES.filter(({ nombre, paquete }) => !(nombre in paquete))

  return (
    <section style={{ padding: 20 }}>
      <h1>React {React.version}</h1>
      <p>{disponibles.length} de {FUNCIONES.length} funciones disponibles</p>
      {faltan.length > 0 && (
        <p>Te faltan: {faltan.map((f) => f.nombre).join(', ')}</p>
      )}
    </section>
  )
}

Texto que muestra la página con dos versiones:

Con React 19.3.1:
React 19.3.1
5 de 5 funciones disponibles

Con React 19.1.10:
React 19.1.10
3 de 5 funciones disponibles
Te faltan: Activity, ViewTransition

Date cuenta de que disponibles y faltan no se guardan en ningún estado: se calculan. Es la misma idea del buscador de la sección 3. Guardar en el estado lo que se puede calcular es uno de los errores más comunes en React.

Ejercicio 7: Elige versión

Decide qué versión usarías en cada caso y justifica la respuesta:

  • a) Empiezas tu proyecto de fin de ciclo con Vite y React Router 8.
  • b) Una empresa tiene una aplicación en React 19.1.4, sin Server Components. No quieren cambios grandes este trimestre, pero sí estar al día en seguridad.
  • c) La misma empresa quiere añadir React Router 8.
  • d) Heredas una aplicación en React 18.2 con clases, propTypes y ReactDOM.render.
  • e) Tu proyecto usa el App Router de Next.js 16.
  • f) Has leído que hay una función nueva disponible solo en canary y quieres probarla.
Ver solución
  • a) La última estable, 19.3.1. Proyecto nuevo y sin restricciones. Comprueba también que tienes Node 22.22.0 o superior, que es lo que pide React Router 8.
  • b) 19.1.10, el último parche de su línea, con npm install react@19.1 react-dom@19.1. Un parche no debería cambiar nada más. Sin Server Components, tampoco le afectaba CVE-2025-55182, pero mantenerse en el último parche es un buen hábito.
  • c) 19.2.7 como mínimo, y mejor la última de la 19.2 o la 19.3. Con 19.1, npm falla con ERESOLVE. No debe usarse --force: hay que subir React, una actualización que semver considera compatible.
  • d) Primero 18.3.1, sin tocar nada más, y corrige los avisos. Luego usa las recetas automáticas y pasa a 19. Las clases siguen funcionando. Lo que hay que cambiar es propTypes y ReactDOM.render, que desaparecen.
  • e) La que marque Next.js. Next.js incluye su propia copia de React para el App Router. Actualiza siguiendo sus instrucciones.
  • f) canary, fijada a una versión exacta y en una rama aparte, nunca en producción. Canary no sigue semver y puede cambiar sin aviso.

Ejercicio 8: Mezcla versiones

Razona qué ocurre en estos casos antes de mirar la solución:

  • a) react 19.2.9 con react-dom 19.3.1.
  • b) react 19.3.1 con react-dom 19.2.9.
  • c) ¿Lo detecta npm al instalar?, ¿y React al ejecutar?
Ver solución

a) y b) En los dos casos, React lanza un error al arrancar. Esta es la salida real del caso a:

Error: Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:
  - react:      19.2.9
  - react-dom:  19.3.1
Learn more: https://react.dev/warnings/version-mismatch

El caso b da el mismo error con los números cambiados. La comprobación es estricta: no importa cuál de los dos sea más nuevo, ni que solo difiera un número. Lo he comprobado con NODE_ENV=development y con NODE_ENV=production, y en el caso a el error aparece en ambos modos.

c) Hay dos niveles de protección:

  • npm lee las dependencias par de react-dom. Como react-dom 19.3.1 pide react: ^19.3.1, avisa si le das una versión menor, como en el caso a.
  • React comprueba al ejecutar que las versiones coinciden exactamente. Esto cubre lo que el rango de npm deja pasar: ^19.3.1 aceptaría un react más moderno que react-dom, pero React lo rechazaría al arrancar.

Por eso la regla es sencilla: actualiza siempre los dos paquetes en la misma orden y a la misma versión.

Ejercicio 9 (propuesto): Amplía tu primera página

Parte de primer-react.html, de la sección 5. Añade un botón «Borrar» que vacíe el nombre y un párrafo que indique cuántas letras tiene. Hazlo con createElement, sin JSX. ¿Qué parte de la pantalla has tenido que actualizar a mano?

Ejercicio 10 (propuesto): Más estados en el buscador

Amplía la versión a mano del buscador con un botón para borrar cada empleado y un total de empleados borrados. Después haz lo mismo en React, en un proyecto creado como explica el módulo 01. Anota cuántas líneas has tenido que cambiar en cada caso, y qué errores de desincronización has cometido.

Ejercicio 11 (propuesto): Ficha de un proyecto real

Elige un proyecto de código abierto con React en GitHub. Abre su package.json y su fichero de bloqueo, y anota: qué versión de React usa, qué rango declara, si es la última de su línea, qué bibliotecas de la sección 8 utiliza, y qué marcas de este curso le afectarían.


Resumen

Concepto API Idea clave
Qué es React react, react-dom Biblioteca para interfaces: componentes en el núcleo y un puente con el navegador
Imperativo createElement, textContent Dices cada paso. Hay dos fuentes de verdad que pueden desincronizarse
Declarativo pantalla = componente(estado) Describes el resultado para cada estado. Lo demás se deriva de él
Render Tu componente Ejecutar el componente y comparar descripciones. No es pintar
Reconciliación Interna Averiguar qué ha cambiado entre dos descripciones
Commit Interna Aplicar al DOM solo las diferencias. Los nodos que no cambian conservan texto y foco
Arranque createRoot(...).render(...) Conecta React con un nodo del DOM
JSX createElement Sintaxis cómoda que se transforma en llamadas a funciones
Semver ^, ~, >= Mayor, menor y parche. ^ admite menores nuevas, y ~ solo parches
Canales latest, canary, experimental latest para aplicaciones; canary para frameworks, fijada; experimental para probar
LTS No existe Solo se promete corregir vulnerabilidades en las mayores afectadas, sin fechas
Parches 19.0.9, 19.1.10, 19.2.9, 19.3.1 Mantente en el último de tu línea. Fijar una versión para siempre te deja sin ellos
Pareja de paquetes npm install react@X react-dom@X Deben tener la misma versión, o React lanza un error
Bibliotecas Dependencias par React Router 8 pide React 19.2.7 y Node 22.22.0 o superiores
Elegir versión Figura 11 Nuevo: última estable. Framework: la suya. Serie 19: último parche. 18: migrar por 18.3.1
Ver tu versión npm ls react, version package.json muestra el rango, no lo instalado
Marcas 🆕 19.x, 🔄 19.x La versión exacta de cada novedad, y qué hacer si tienes una anterior