¿Qué versión elegir? Novedades por versión

El curso usa React 19.3, la última versión estable. React no tiene versiones LTS: su política solo promete parches de seguridad para las versiones mayores afectadas. Hoy reciben parches las líneas 19.0, 19.1, 19.2 y 19.3, y la mayor anterior es la 18.3.

Esta página reúne todos los apartados del curso que dependen de la versión. Si tu proyecto o tu centro usan una versión anterior, sabrás qué no tendrás y qué hacer en su lugar. La versión indicada es en la que apareció cada cosa, de modo que también te sirve si usas 19.0, 19.1 o 19.2.

Marca Significado
🆕 Es nuevo en esa versión: no existe en las anteriores
🔄 Ya existía, pero cambia en esa versión

⚠️ Importante: React Router 8 exige React 19.2.7 o superior. Con una versión anterior tienes que usar React Router 7, como explica el módulo 18.

Resumen

El curso tiene 138 apartados que dependen de la versión, en 25 módulos.

Versión 🆕 Nuevo 🔄 Cambia
19.0 53 30
19.1 2 0
19.2 14 5
19.3 22 12

Detalle por módulo

00 - Introducción a React

Apartado Versión Qué ocurre Si usas una versión anterior
React 19.0 🆕 19.0 No disponible 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.
Lo que eliminó React 19.0 🔄 19.0 En React 18.3 y anteriores estas API siguen disponibles, con avisos en 18.3. 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.
React 19.1 🆕 19.1 No disponible 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.
React 19.2 🆕 19.2 No disponible 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.
React 19.3 🆕 19.3 No disponible 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.
Cambios de comportamiento de React 19.3 🔄 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. 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.

02 - JSX a fondo

Apartado Versión Qué ocurre Si usas una versión anterior
La forma del elemento 🔄 19.0 En React 18.3 el símbolo es react.element y ref es un campo propio del elemento. Desde 19.0 el símbolo es react.transitional.element y ref se lee en element.props.ref. Lee element.ref. Solo te afecta si inspeccionas elementos a mano, algo que el código normal no debería hacer.
bigint como hijo 🔄 19.0 Un bigint dentro de llaves se pinta como un número. En React 18.3 no pintaba nada, sin ningún error. Conviértelo a texto con {valor.toString()}.
Elementos personalizados 🔄 19.0 React asigna propiedades a los elementos personalizados (Web Components) si el elemento las define, entiende className y trata true y false como atributos booleanos. En React 18.3 todo se convertía en atributos de texto. Escribe class en lugar de className y pasa objetos o funciones con un ref y elemento.propiedad = valor.
Fragment con ref 🆕 19.3 No disponible Envuelve el grupo en un elemento real, como <div style={{ display: 'contents' }} ref={...}>, o usa un ref en cada hijo. No hay equivalente sin un nodo extra.
URLs javascript: 🔄 19.0 React bloquea las URL javascript: en atributos como href, src y action: las sustituye por código que lanza un error. En React 18.3 las deja pasar y solo avisa en la consola. Valida tú cada URL que venga de datos externos con una lista de protocolos permitidos, como hace urlSegura.
Cuándo se reasigna innerHTML 🔄 19.3 React no vuelve a asignar innerHTML si la cadena __html no ha cambiado, aunque el objeto sea nuevo. Hasta 19.2 lo reasignaba en cada render en el que cambiara el objeto, y destruía los nodos internos. Guarda el objeto { __html } en una constante o con useMemo para que no cambie entre renders.
El espacio de nombres JSX 🔄 19.0 @types/react 19 eliminó el espacio de nombres JSX global. En @types/react 18.3 funcionan las dos formas, así que puedes escribir ya React.JSX.Element, o importar JSX de react, y migrar sin cambios.

03 - Componentes y props

Apartado Versión Qué ocurre Si usas una versión anterior
defaultProps ya no funciona en funciones 🔄 19.0 En React 18.3, defaultProps aún funciona en componentes función, con un aviso de que se retirará; desde 19.0 se ignora sin ningún aviso. Funcionará, pero pasa ya a valores por defecto con desestructuración, que sirven en todas las versiones.
11. ref como prop 🆕 19.0 : allí ref no llega al componente función. Envuelve el componente en forwardRef, que recibe la ref como segundo parámetro, tal como se muestra en esta sección.
Ejercicio 6: Campo reutilizable con ref 🆕 19.0 No disponible Resuelve el ejercicio envolviendo CampoFormulario en forwardRef, que es justo lo que pide el ejercicio 7 a la inversa.

05 - Eventos y ciclo de renderizado

Apartado Versión Qué ocurre Si usas una versión anterior
Saber qué botón envió el formulario 🆕 19.3 En React 19.2 y anteriores el evento sintético de submit no tiene submitter: vale undefined. Lee e.nativeEvent.submitter, que existe en todas las versiones, y con tipos antiguos convierte antes: (e.nativeEvent as globalThis.SubmitEvent).submitter.
Eventos de pantalla completa 🆕 19.3 No existen en React 19.2 ni anteriores. Escucha fullscreenchange y fullscreenerror con addEventListener en un efecto (módulo 09), que hace lo mismo con unas líneas más.
Eventos de fin de scroll y de popover 🆕 19.0 En React 18.3 no existen onScrollEnd ni onBeforeToggle, ni se reconoce la API popover. Escucha scrollend, beforetoggle y toggle con addEventListener en un efecto, y controla el atributo popover desde JavaScript.
Conservar el estado de lo que no se ve 🆕 19.2 No disponible Oculta el componente con CSS (hidden) para conservar su estado, sabiendo que sus efectos seguirán activos, o eleva el estado y deja que el componente se destruya.
Doble llamada a los ref callbacks 🔄 19.0 En React 18.3, Strict Mode llama una sola vez a un ref callback al montar; desde 19.0 lo llama, lo limpia y lo vuelve a llamar. No verás esa repetición, así que comprueba a mano que la limpieza de tus refs funciona.
Doble efecto al hidratar 🔄 19.3 En React 19.0, 19.1 y 19.2, Strict Mode no repite los efectos al hidratar con hydrateRoot. Comprueba la limpieza también en un árbol creado con createRoot, donde la repetición sí ocurre.

06 - Renderizado condicional y listas

Apartado Versión Qué ocurre Si usas una versión anterior
Conservar el estado con <Activity> 🆕 19.2 No disponible Usa el atributo hidden, que también conserva el estado, aunque los efectos del panel oculto seguirán activos. En el ejemplo de esta sección, borra el bloque 3 y su importación.

07 - Estilos en React

Apartado Versión Qué ocurre Si usas una versión anterior
9. <style> y <link> en componentes 🆕 19.0 No disponible <style> y <link> se quedan en el <body>, se repiten con cada instancia y precedence acaba como atributo sin efecto. Importa el CSS con import o colócalo en index.html, y carga los estilos bajo demanda con import().
10. Metadatos de documento 🆕 19.0 No disponible <title> y <meta> se quedan en el <body> y el título de la pestaña no cambia. Asigna document.title dentro de un useEffect, o usa una biblioteca que gestione <head>.

08 - Formularios

Apartado Versión Qué ocurre Si usas una versión anterior
Saber qué botón se pulsó 🆕 19.3 : allí e.submitter es undefined. Lee e.nativeEvent.submitter, que da el mismo botón en todas las versiones que hemos probado.
defaultValue en campos numéricos 🔄 19.3 Hasta React 19.2, si un <input type="number"> tiene el foco, cambiar defaultValue no actualiza su atributo value; en 19.3 sí lo hace, igual que en los demás tipos de campo. No cambies defaultValue con el campo enfocado; para reiniciarlo con otro valor, cámbiale la key.
Formato del identificador 🔄 19.2 useId genera _r_0_. En 19.1 generaba «r0», y en 19.0 y 18.3 generaba :r0:. El hook funciona igual, pero si usas el identificador en un selector CSS o en querySelector, escápalo con CSS.escape(id), porque los dos puntos no son válidos en un selector sin escapar.
10. <form action>: enviar con una función 🆕 19.0 No existe en React 18.3: allí React avisa con Invalid value for prop action, descarta la función y el navegador envía el formulario recargando la página. Usa onSubmit con e.preventDefault(), new FormData(e.currentTarget) y un estado enviando, como en la sección 9.
onReset tras una Action 🔄 19.3 El reinicio automático tras una Action dispara onReset. En 19.0, 19.1 y 19.2, el formulario se reinicia sin disparar ese evento. No pongas lógica de limpieza en onReset para este caso; ejecútala al final de la propia Action.
Ejercicio 8: Dos acciones sobre un formulario 🆕 19.0 No existe en React 18.3. Usa onSubmit, un solo manejador y e.nativeEvent.submitter para distinguir el botón.

09 - Efectos con useEffect

Apartado Versión Qué ocurre Si usas una versión anterior
Hidratación y Fast Refresh 🔄 19.3 En React 19.0, 19.1 y 19.2, Strict Mode no repite los efectos al hidratar con hydrateRoot ni al remontar un componente con Fast Refresh. Esos dos casos no te avisarán de una limpieza que falta, así que comprueba el componente también en un árbol creado con createRoot, donde la repetición sí ocurre.
Efectos que paran y vuelven: <Activity> 🆕 19.2 No disponible No hay equivalente exacto. Para ocultar una parte de la interfaz sin perder su estado, elévalo a un componente padre o usa display: none; en ese caso los efectos siguen activos.
El título de la pestaña 🆕 19.0 : allí un <title> se queda en el <body> y no cambia la pestaña. Usa un efecto con document.title, como el que se muestra aquí debajo.
9. useEffectEvent: lógica que no reacciona 🆕 19.2 : al llamarlo obtienes useEffectEvent is not a function. Guarda la función en una ref actualizada en un useLayoutEffect, como en el apartado “Si no tienes React 19.2” de esta sección.
Dentro de memo y forwardRef 🔄 19.3 En React 19.2, un efecto de evento dentro de un componente envuelto en memo o forwardRef lee valores antiguos. Quita el memo de ese componente, o usa el patrón de la ref del apartado siguiente, que no tiene este problema.
Con renderizado en servidor 🔄 19.0 En React 18.3 y anteriores, renderToString escribe en consola useLayoutEffect does nothing on the server. Ignora el aviso si el contenido es igual en servidor y cliente, o usa useEffect en ese componente.
13. Caso práctico: panel de empleados 🆕 19.2 Este caso usa useEffectEvent, que no existe en React 19.1 ni anteriores. Sustituye refrescar por una ref actualizada en useLayoutEffect, como en el apartado “Si no tienes React 19.2” de la sección 9. El resto funciona igual.
Ejercicio 6: Monitor de estadísticas 🆕 19.2 Este ejercicio usa useEffectEvent, que no existe en React 19.1 ni anteriores. Guarda la función alRecibir en una ref actualizada en useLayoutEffect, como en la sección 9.

10 - Refs, DOM y portales

Apartado Versión Qué ocurre Si usas una versión anterior
useRef exige un argumento 🔄 19.0 Con @types/react 18 se podía escribir useRef<number>(), y los tipos distinguían un RefObject de solo lectura y un MutableRefObject. Con los tipos de 19, el argumento es obligatorio y solo queda RefObject, con current siempre modificable. Pasa siempre un argumento, por ejemplo useRef<number \| undefined>(undefined), que compila en ambas.
La limpieza de refs 🆕 19.0 : la función devuelta se ignora y sale un aviso en consola. No devuelvas nada y limpia cuando la función reciba null, guardando lo necesario en otra ref.
Función en línea y función estable 🆕 19.0 El ejemplo devuelve una limpieza, que no existe en React 18.3. Quita los return; verás una llamada con null en lugar de cada línea limpiar.
Strict Mode ensaya también las refs 🔄 19.0 En desarrollo, Strict Mode enlaza, suelta y vuelve a enlazar las refs al montar. En React 18.3 no lo hace: la llamada es una sola. No tienes que cambiar nada, pero tampoco verás los errores que este ensayo destapa, así que escribe tus limpiezas igual de bien.
El retorno implícito 🔄 19.0 Con @types/react 18, una callback ref podía devolver cualquier valor. Con los de 19, solo void o una función de limpieza. Tu código compila, pero añade llaves igualmente, porque así seguirá compilando al actualizar.
Una lista de nodos 🆕 19.0 El ejemplo limpia el Map devolviendo una función, algo que React 18.3 ignora. Borra la entrada cuando la función reciba null, como muestra el final de este apartado.
5. Refs en tus propios componentes 🆕 19.0 : ref no llega como prop a un componente función. Envuelve el componente en forwardRef, que recibe la ref como segundo parámetro.
6. useImperativeHandle 🔄 19.0 El hook existe desde hace años, pero los ejemplos de esta sección reciben ref como prop. Envuelve el componente en forwardRef y pasa a useImperativeHandle la ref de su segundo parámetro; el resto del código es igual.
9. Fragment Refs 🆕 19.3 : <Fragment ref> da un aviso y la ref no recibe nada. Añade un envoltorio con ref (en una tabla, un <tbody> por grupo), o un <div style={{ display: 'contents' }}>, que no afecta al diseño; también puedes usar una callback ref por hijo con un Map, como en la sección 4.
10. Caso práctico: panel de empleados 🆕 19.0 Este ejemplo usa ref como prop y la limpieza de refs. Envuelve Buscador en forwardRef, y en la ref de cada fila borra del Map cuando el argumento sea null en lugar de devolver una limpieza.
Ejercicio 5: Observar con una callback ref 🆕 19.0 La limpieza devuelta por la callback ref no existe en React 18.3. Guarda cada observador en un Map y desconéctalo cuando la función reciba null.
Ejercicio 6: Carrusel controlado desde fuera 🆕 19.0 La solución recibe ref como prop. Envuelve Carrusel en forwardRef.
Ejercicio 8: Foco en un grupo 🆕 19.3 Usa Fragment Refs, que no existen en React 19.2 ni anteriores. Envuelve el grupo en un <div ref={...}> y usa querySelector('input')?.focus().

11 - useReducer y Context

Apartado Versión Qué ocurre Si usas una versión anterior
Tipos y genéricos de useReducer 🔄 19.0 Los tipos de @types/react 19 cambian los genéricos de useReducer: ahora son useReducer<Estado, [Accion]>, y ya no useReducer<Reducer<Estado, Accion>>. Deja que TypeScript infiera los tipos desde el reductor, que funciona igual en ambas versiones. Si necesitas escribirlos a mano con @types/react 18, usa useReducer<Reducer<Estado, Accion>>, y Dispatch<Accion> para tipar dispatch, porque ActionDispatch no existe en esos tipos.
10. El proveedor: <Context> 🆕 19.0 El propio contexto actúa como proveedor: <Contexto value={x}>. No existe en React 18.3 ni anteriores. Escribe <Contexto.Provider value={x}>. En 18.3.1, <Contexto> se trata como un consumidor y la aplicación se rompe con render is not a function.
<Context> en Server Components 🆕 19.3 Un Server Component puede renderizar <Context> directamente. No existe en React 19.2 ni anteriores. Coloca el proveedor en un componente propio marcado con 'use client' y úsalo desde el Server Component.
11. Leer el contexto: use(Contexto) 🆕 19.0 use(Contexto) lee un contexto y admite condiciones y bucles. No existe en React 18.3 ni anteriores: el paquete no exporta use. Lee con useContext(Contexto) al principio del componente, antes de cualquier return condicional. Se pierde la posibilidad de leer el contexto dentro de condiciones o bucles.
Ejercicio 4: Control por roles con use 🆕 19.0 Este ejercicio usa use y <Contexto>. No existen en React 18.3 ni anteriores. Usa <Contexto.Provider>, y lee con useContext al principio del componente, antes de if (deshabilitado).

12 - Hooks personalizados

Apartado Versión Qué ocurre Si usas una versión anterior
Con useEffectEvent 🆕 19.2 No disponible Guarda la función en una ref y actualízala en useLayoutEffect. Es la versión compatible que aparece al final de este apartado y se comporta igual.
11. Hooks que devuelven una ref 🆕 19.0 Los callback refs pueden devolver una función de limpieza. En React 18 ese valor se ignora y el callback se llama con null al desmontar. Guarda el ResizeObserver en una useRef y desconéctalo en la rama nodo === null.
Ejercicio 4: useCuentaAtras 🆕 19.2 No disponible Importa la versión compatible de useIntervalo de la sección 10, basada en una ref. El resto del hook no cambia.
Ejercicio 5: useClickFuera 🆕 19.2 No disponible Guarda alClicarFuera en una ref actualizada en useLayoutEffect y llama a ref.current() dentro del efecto, como en la versión compatible de useIntervalo.

13 - TypeScript con React

Apartado Versión Qué ocurre Si usas una versión anterior
El namespace JSX ya no es global 🔄 19.0 Los tipos de React 19 ya no declaran JSX como global: JSX.Element da el error Cannot find namespace 'JSX'. El JSX global sigue existiendo, pero React.JSX.Element funciona en 18.3 y en 19, así que escríbelo siempre de esa forma.
Las props de ReactElement son unknown 🔄 19.0 En los tipos de React 19, ReactElement sin parámetros tiene props: unknown, cuando antes era any. El código compila sin indicar el tipo, pero declarar ReactElement<{ titulo: string }> funciona en todas las versiones y es más seguro.
SubmitEvent sustituye a FormEvent 🔄 19.2 Solo afecta a los tipos: desde @types/react 19.2.10 existen SubmitEvent y SubmitEventHandler, y FormEvent está marcado como obsoleto. Esos tipos no existen, así que escribe FormEvent<HTMLFormElement> en onSubmit, que funciona igual y sigue compilando en 19.3.
submitter en el evento de React 🔄 19.3 El evento submit de React expone submitter con el botón que lo envió. e.submitter compila con @types/react 19.2.10 o superior, pero en ejecución vale undefined; lee e.nativeEvent.submitter, que funciona en todas las versiones.
useRef exige argumento 🔄 19.0 useRef necesita un argumento y RefObject deja de ser de solo lectura, aunque se cree con null. useRef<T>() sin argumento compila, y useRef<T>(null) devuelve un objeto con .current de solo lectura; para escribir en él declara useRef<T \| null>(null).
ref como prop 🆕 19.0 Un componente función recibe ref como una prop normal y se tipa con Ref<T>. No existe en React 18.3 ni anteriores, donde ref no llega a las props. Envuelve el componente con forwardRef, como se muestra a continuación; en esas versiones usa ComponentPropsWithoutRef para las props.
Retorno de una ref función 🔄 19.0 Una función ref puede devolver una función de limpieza, y por eso los tipos rechazan que devuelva cualquier otra cosa. El retorno se ignora, no hay limpieza y debes tratar el caso en que la función recibe null.
Parámetros de tipo de useReducer 🔄 19.0 Los tipos de useReducer cambian: ahora son useReducer<S, A>, donde A es una tupla de argumentos, y aparece ActionDispatch. Se escribe useReducer<Reducer<Estado, Accion>>. Lo mejor es no escribir parámetros: useReducer(reductor, inicial) compila igual en todas las versiones.
<Context> y use 🆕 19.0 <Contexto value> sustituye a <Contexto.Provider value>, y use(Contexto) lee el contexto. No existen en React 18.3 ni anteriores. Escribe <SesionContexto.Provider value={valor}> y useContext(SesionContexto); todo lo demás, incluidos los tipos, es igual.

14 - Rendimiento y React Compiler

Apartado Versión Qué ocurre Si usas una versión anterior
Compilador con React 17 o 18 🔄 19.0 Desde 19.0, React trae el runtime del compilador en react/compiler-runtime; en 18.3 esa ruta no existe y la importación falla. Instala react-compiler-runtime como dependencia normal, no de desarrollo, y añade target: '18' o target: '17' a las opciones del compilador.
Medir en producción 🔄 19.0 El cliente de react-dom es desde 19.0 un módulo propio, así que el alias se pone sobre react-dom/client. En React 18, react-dom/client importa internamente react-dom, y el alias exacto se pone sobre react-dom: { find: /^react-dom$/, replacement: 'react-dom/profiling' }.
Performance Tracks 🆕 19.2 No disponible Usa el Profiler de React DevTools y <Profiler>. Pierdes ver el trabajo de React en la misma línea de tiempo que la red y el JavaScript del navegador.

15 - Suspense y concurrencia

Apartado Versión Qué ocurre Si usas una versión anterior
Transiciones asíncronas 🔄 19.0 En React 18.3 la función debía ser síncrona. Lleva tú mismo un estado cargando con useState, haz la petición fuera de la transición y envuelve solo el setState final en startTransition.
Errores dentro de una transición 🔄 19.0 Antes no había transiciones asíncronas ni un camino para llevar sus errores a una Error Boundary. Rodea la operación con try/catch y guarda el error en un estado que el componente muestre.
Transiciones independientes 🔄 19.3 Hasta React 19.2 las transiciones simultáneas se agrupan y una lenta retiene a las demás. No hay alternativa directa. Evita lanzar a la vez transiciones que no tengan relación, o saca fuera de la transición la que deba verse enseguida.
Valor inicial 🔄 19.0 El segundo argumento initialValue no existe en React 18.3, donde el primer render ya usa el valor real. Guarda el valor en un estado inicializado con el marcador y actualízalo en un efecto dentro de startTransition.
Hermanos: el fallback ya no espera 🔄 19.0 En React 18.3 se renderizan todos los hermanos antes de mostrar el fallback. La interfaz funciona igual, pero el fallback tarda más en verse si los hermanos son caros de renderizar. No necesitas cambiar tu código.
Retardo entre revelados 🔄 19.0 Según el changelog, el retardo baja de 500 ms a 300 ms. Funciona igual, solo que con una espera mayor.
7. use(promesa): leer datos en el render 🆕 19.0 No disponible Carga los datos con useEffect y un estado cargando, como en el módulo 09, o usa useSuspenseQuery de TanStack Query, que es compatible con React 18.
Aviso por use condicional 🔄 19.3 Añade un aviso en desarrollo que no existe en 19.2.9. No verás el aviso, pero el patrón es igual de incorrecto, así que evítalo también.
8. Suspense con transiciones y valores diferidos 🆕 19.0 Los ejemplos de este apartado leen datos con use. La combinación de transición y Suspense ya existe en React 18.3 con lazy o con bibliotecas compatibles. Aplica estas mismas ideas con lazy, o con useSuspenseQuery de TanStack Query.
Cómo se notifican los errores 🔄 19.0 En React 18.3 los errores se relanzan y cada uno llega varias veces a window. Usa componentDidCatch para registrar y filtra los errores repetidos en tu servicio de monitorización.
Opciones de la raíz 🆕 19.0 onCaughtError y onUncaughtError no existen en React 18.3. Registra en componentDidCatch los errores capturados, y escucha el evento error de window para los demás.
captureOwnerStack 🆕 19.1 No disponible Tienes info.componentStack en componentDidCatch, que muestra la jerarquía, pero no quién creó cada componente.
11. Caso completo: panel de EMPRESA 🆕 19.0 Este caso lee datos con use. Sustituye use(leer(...)) por useSuspenseQuery de TanStack Query (módulo 19); la estructura de transición, Suspense y frontera de error no cambia.

16 - Actions

Apartado Versión Qué ocurre Si usas una versión anterior
Todo el módulo 🆕 19.0 Casi todo este módulo depende de las Actions, que no existen en React 18.3 ni anteriores. Escribe el ciclo a mano con useState, try, catch y finally, como en el primer ejemplo de la sección 1, y actualiza a 19 cuando puedas. Los cambios de 19.1, 19.2 y 19.3 no alteran nada de lo que se enseña aquí: hemos ejecutado las pruebas de comportamiento en 19.0.9, 19.2.9 y 19.3.1 y el resultado coincide.
2. Acciones asíncronas en transiciones 🆕 19.0 : startTransition acepta la función asíncrona, pero no espera a la promesa, e isPending vuelve a false enseguida. Mantén un estado guardando con useState y apágalo en un finally.
3. useActionState: el resultado como estado 🆕 19.0 No disponible Usa useReducer o useState más un estado de espera propio, y gestiona a mano el orden de las peticiones. En las versiones canary previas a 19 se llamaba useFormState y estaba en react-dom.
4. useFormStatus: el formulario visto desde dentro 🆕 19.0 : react-dom 18.3.1 no lo exporta. Pasa la marca de envío por props o con un Context propio, como en el módulo 11.
5. useOptimistic: no hagas esperar al usuario 🆕 19.0 No disponible Guarda el valor nuevo con useState antes de la petición, conserva una copia del anterior y restáuralo en el catch. Tendrás que resolver a mano las operaciones que se solapan.
6. requestFormReset: decidir cuándo se vacía 🆕 19.0 No disponible Llama a formulario.reset() del DOM cuando termine la operación.

17 - Activity, View Transitions y más

Apartado Versión Qué ocurre Si usas una versión anterior
2. <Activity>: ocultar sin perder 🆕 19.2 , donde tampoco hay una versión unstable_. Oculta con style={{ display: 'none' }}, que conserva estado y DOM pero deja los efectos activos, o eleva el estado al padre si te basta con guardar los datos.
3. cacheSignal: cancelar trabajo sobrante 🆕 19.2 No disponible Crea tu propio AbortController y llama a abort() cuando sepas que el trabajo ya no sirve. No hay forma directa de enterarse de cuándo acaba el render en servidor.
4. <ViewTransition>: animar cambios de interfaz 🆕 19.3 No disponible Llama tú a document.startViewTransition y aplica el cambio dentro con flushSync, como se explica en el apartado 13. Pierdes la integración con transiciones, Suspense y nombres automáticos.
5. Los cuatro disparadores 🆕 19.3 No disponible Distingue tú los casos con document.startViewTransition, poniendo y quitando view-transition-name a mano, como en el apartado 13.
6. Personalizar con clases CSS 🆕 19.3 No disponible Asigna view-transition-class o view-transition-name en tu CSS y usa document.startViewTransition. Los selectores ::view-transition-* funcionan igual.
7. addTransitionType: animar según la causa 🆕 19.3 No disponible Guarda la dirección en una variable de estado antes de navegar, o pásala a document.startViewTransition({ update, types }) si llamas tú a esa API.
8. Elementos compartidos con share 🆕 19.3 No disponible Pon el mismo view-transition-name en los dos elementos, uno antes y otro después del cambio, dentro de document.startViewTransition con flushSync.
9. Reordenar listas 🆕 19.3 No disponible Asigna un view-transition-name distinto a cada fila y ordena dentro de document.startViewTransition con flushSync.
10. Suspense y View Transitions 🆕 19.3 No disponible No hay alternativa directa, porque tu código no se entera del momento de la revelación. Puedes animar el contenido con CSS al montarse.
11. Animar con JavaScript 🆕 19.3 No disponible Espera a la promesa ready de document.startViewTransition y anima con document.documentElement.animate y la opción pseudoElement.
12. View Transitions con React Router 🆕 19.3 No disponible Usa la prop viewTransition de <Link>, que llama a document.startViewTransition por su cuenta, aunque React Router 8 exige React 19.2.7 o superior.
14. browser(): solo en el navegador 🆕 19.3 No disponible Usa un estado montado que se activa en useEffect y pinta un sustituto hasta entonces. Pierdes el aviso onBrowserBailout y haces un render de más.
15. Trusted Types 🔄 19.3 dangerouslySetInnerHTML ya aceptaba objetos TrustedHTML antes, pero srcDoc no. Usa dangerouslySetInnerHTML con una política, que funciona igual desde 18.3, y asigna srcdoc con una ref en lugar de la prop.
Transiciones independientes 🔄 19.3 Hasta React 19.2 las transiciones simultáneas se agrupan y una lenta retiene a las demás. No hay alternativa directa. Evita lanzar a la vez transiciones sin relación, o saca de la transición la que deba verse enseguida.
17. Caso completo: panel de EMPRESA 🆕 19.3 No disponible Quita <ViewTransition> y addTransitionType. El panel sigue funcionando sin animaciones desde 19.2, y antes de 19.2 cambia <Activity> por display: none.

18 - React Router 8

Apartado Versión Qué ocurre Si usas una versión anterior
Proteger con middleware 🔄 19.2 Con React 19.2.7 o superior y React Router 8, en React Router 8 middleware es una función estable y context siempre tiene su tipo. En React Router 7.18 pasa future: { v8_middleware: true } a createBrowserRouter, como pide su documentación, y amplía interface Future para que context no sea any. Los detalles están en la sección 20.
Transiciones de vista entre pantallas 🆕 19.3 <ViewTransition> no existe en React 19.2 ni anteriores. Usa la prop viewTransition de <Link>, <NavLink> y <Form>. Está disponible en React Router 7 y 8, aunque en 8.5 quedó obsoleta. Sigue funcionando y no exige React 19.3, pero se apoya en la API de React Router, que algún día desaparecerá.
20. React Router 7: la alternativa para React anterior a 19.2.7 🔄 19.2 React Router 8 exige React 19.2.7 y no admite React 19.2.6 ni anteriores. Instala react-router@7.18.5. Este módulo funciona casi sin cambios: la aplicación EMPRESA se ha recorrido con esa versión sobre React 19.1.10 y 18.3.1, con los mismos resultados.

19 - Datos con TanStack Query

Apartado Versión Qué ocurre Si usas una versión anterior
Precargar en el loader 🆕 19.2 React Router 8 no se instala en React 19.1 ni anteriores: exige react >= 19.2.7. El patrón sigue siendo válido sin Router 8, porque solo consiste en llamar a queryClient.query antes de cambiar de pantalla: hazlo en el manejador que cambia de vista, o en onMouseEnter, y deja que el componente lea con useSuspenseQuery.
Comparación con useOptimistic 🆕 19.0 useOptimistic y useActionState no existen en React 18.3 ni anteriores. Usa onMutate de Query, como en la sección 11, o pinta las variables de la mutación; funcionan igual en React 18.

20 - Estado global: Zustand y Redux Toolkit

Apartado Versión Qué ocurre Si usas una versión anterior
1.3. Por qué Context no basta 🆕 19.0 <Contexto value> no existe en React 18.3. Escribe <Contexto.Provider value>. Todo lo que se explica aquí sobre los renders funciona igual.
8. Un store por rama del árbol 🆕 19.0 El ejemplo usa <ContextoFiltro value>, que no existe en React 18.3. Escribe <ContextoFiltro.Provider value>. El resto funciona igual, porque createStore y useStore no dependen de la versión de React.

21 - Formularios avanzados

Apartado Versión Qué ocurre Si usas una versión anterior
Pasar field.ref a tu componente 🆕 19.0 ref es una prop normal; antes no llegaba a los componentes función. Envuelve Estrellas con forwardRef<HTMLButtonElement, PropsEstrellas>((props, ref) => ...) y quita ref de la interfaz de props.
Un campo reutilizable 🆕 19.0 ref llega como una prop más y {...resto} la entrega al <input>. Envuelve Entrada con forwardRef<HTMLInputElement, PropsEntrada> y añade ref={ref} al <input>, o register no recibirá el nodo y el campo fallará sin avisar.
16. Comparación con las Actions 🆕 19.0 useActionState, useFormStatus, <form action> y las transiciones asíncronas no existen en 18.3. El enfoque de esta sección no está disponible. Usa RHF con handleSubmit, que funciona igual, y gestiona la espera con isSubmitting.

22 - Testing

Apartado Versión Qué ocurre Si usas una versión anterior
Por qué desapareció react-test-renderer 🔄 19.0 react-test-renderer/shallow se elimina, react-dom/test-utils conserva solo act (obsoleto) y el resto de utilidades, como Simulate, desaparece. En 18.3 esas utilidades aún existen, pero no las uses; escribe las pruebas con Testing Library, que funciona igual en 18.3 y en 19, e importa act de react o de @testing-library/react.
El proveedor de sesión 🆕 19.0 <SesionContext value={...}> no existe en React 18. Escribe <SesionContext.Provider value={...}>. Las pruebas son idénticas, porque no dependen de esa sintaxis.
Actions 🆕 19.0 useActionState, useFormStatus y <form action={función}> no existen en React 18. Escribe el login con onSubmit, useState para el error y otro useState para el estado pendiente. Las pruebas de esta sección valen sin cambios, porque solo miran la pantalla.
Suspense y use 🆕 19.0 use(promesa) no existe en React 18. Carga los datos con un efecto o con useSuspenseQuery. La parte de lazy y Suspense funciona igual en 18.3, y una prueba con findBy también.

23 - Server Components y Next.js 16

Apartado Versión Qué ocurre Si usas una versión anterior
Todo el módulo 🆕 19.0 Server Components, Server Functions y las directivas 'use client' y 'use server' son estables desde esta versión. No existen como función estable en React 18.3 ni anteriores. No hay alternativa directa con Vite y una SPA. Puedes hacer SSR clásico con renderToPipeableStream, o usar el Pages Router de Next con getServerSideProps, pero pierdes los Server Components. Con el App Router la versión de tu package.json importa menos de lo que parece, como verás en el apartado 4.
3. Qué son los Server Components 🆕 19.0 No existen como función estable en React 18.3 ni anteriores. Usa SSR clásico o datos con fetch desde el cliente, como en el módulo 19. No tendrás componentes que se ejecuten solo en el servidor.
Renderizar <Context> en el servidor 🆕 19.3 Un Server Component puede renderizar directamente un <Context> importado de un módulo 'use client'. No existe en React 19.2 ni anteriores. Crea un Client Component envoltorio, como TemaProvider, que reciba value y children y renderice el contexto, e impórtalo desde el servidor.
9. 'use server' y Server Functions 🆕 19.0 No existen como función estable en React 18.3 ni anteriores. Crea un punto de entrada en tu API y llámalo con fetch desde un manejador, como hiciste en el módulo 16.
Pasar una promesa al cliente con use 🆕 19.0 No disponible Resuelve los datos en el Server Component y pásalos como prop normal. Pierdes el streaming de esa parte.

24 - Accesibilidad e internacionalización

Apartado Versión Qué ocurre Si usas una versión anterior
inert: apagar una zona completa 🔄 19.0 En 18.3.1, inert={true} no llega al DOM y React muestra un aviso, y en 19 el truco antiguo inert="" deja de funcionar. Escribe inert="" para activarlo y inert={undefined} para quitarlo, y añade un // @ts-expect-error si tus tipos no lo admiten.
Anunciador global 🆕 19.0 <Contexto value> y use(Contexto) no existen en 18.3. Escribe <Contexto.Provider value> y useContext(Contexto), que hacen lo mismo. El resto del anunciador no cambia.
Saltar al contenido y cambios de ruta 🆕 19.0 <title> dentro de un componente, que React lleva al <head>, no existe en 18.3. Usa un efecto con document.title = titulo. Ten en cuenta también que React Router 8 exige React 19.2.7 o superior.
Enfocar un grupo con Fragment Refs 🆕 19.3 <Fragment ref> no existe en React 19.2 ni anteriores. Pon una ref en un contenedor real y busca dentro con querySelector('input, select, textarea, button, a[href]')?.focus().
Mi propio t() 🆕 19.0 <Contexto value> y use(Contexto) no existen en 18.3. Escribe <Contexto.Provider value> y useContext(Contexto).
14. Caso completo: tabla de empleados en tres idiomas 🆕 19.0 Este ejemplo usa <title> dentro de un componente. Sustitúyelo por document.title = t('app') en el efecto que actualiza lang y dir.

25 - Arquitectura y despliegue

Apartado Versión Qué ocurre Si usas una versión anterior
La implementación 🆕 19.0 <Contexto value> no existe en React 18.3 ni anteriores. Escribe <ContextoPestanas.Provider value={...}>, que sigue funcionando y hace lo mismo.
Enlaces javascript: 🔄 19.0 En React 18.3, <a href="javascript:alert(1)"> llega tal cual al DOM, con solo un aviso en desarrollo, y al pulsarlo se ejecuta. Valida las URL que vengan de los datos antes de pintarlas, por ejemplo con new URL(valor).protocol, y acepta solo una lista de esquemas conocidos.
onUncaughtError y onCaughtError 🆕 19.0 Estas opciones de createRoot no existen en React 18.3 ni anteriores. Informa de los errores en el componentDidCatch de una Error Boundary que envuelva toda la aplicación, y usa window.addEventListener('error', ...) para el resto.

26 - Proyecto final

Apartado Versión Qué ocurre Si usas una versión anterior
Campos de formulario y ref como prop 🆕 19.0 En React 18.3, ref no llega a un componente función como prop. Envuelve Campo y Desplegable con forwardRef, como se muestra en la sección 17. Sin ese cambio, React Hook Form no recibe la referencia del <input> y el formulario se envía siempre vacío.
Títulos de documento 🆕 19.0 React 18.3 no sube <title> al <head>: lo pinta dentro del <body> y el título de la pestaña no cambia. Sustituye la etiqueta por useEffect(() => { document.title = ... }, [titulo]).
17. Si tu proyecto usa React 19.1 o anterior 🔄 19.2 React Router 8 exige React 19.2.7 o superior y Node 22.22 o superior. Cambia react-router por la versión 7.18.5; con React 19.0 o 19.1 no hay que tocar nada más, y con React 18.3 hay que añadir forwardRef a los dos campos de formulario y sustituir <title> por document.title.