Separar servidor de cliente: el patrón de 2026
El nivel entero fue una sola tesis vista desde cuatro ángulos: el estado de servidor es una cache, y una cache no es un store. Esta lección sube al plano donde todo encaja: la taxonomía completa del estado y la herramienta de cada clase, cuánto encoge el store global cuando el servidor se va, por qué separar las dos clases por su propiedad elimina categorías enteras de bugs de sincronización, el stack por defecto de 2026 y cómo los componentes de servidor empujan el patrón aún más lejos. Menos estado global, menos bugs: el patrón dominante de la industria y por qué ganó.
Cerramos el nivel subiendo al plano donde todo encaja. Las cuatro lecciones anteriores fueron, en el fondo, una sola tesis vista desde cuatro ángulos: el estado de servidor es una cache, y una cache no es un store. La consecuencia práctica de tomarse eso en serio es una de las decisiones de arquitectura más rentables que puedes tomar en 2026: sacar del estado global todo lo que en realidad era servidor disfrazado, dárselo a una cache que sabe tratarlo, y quedarte con un estado de cliente tan pequeño y tan claro que por fin se entiende de un vistazo. Menos estado global, menos bugs. Este es el patrón dominante de la industria, y esta lección explica por qué ganó.
- Ver la taxonomía completa del estado y qué herramienta gobierna cada clase.
- Entender qué queda en el store de cliente cuando el servidor se va.
- Explicar por qué separar las dos clases elimina categorías enteras de bugs.
- Situar el stack dominante de 2026 y el papel de los componentes de servidor.
Dos clases, dos herramientas
El error que este nivel combate era meterlo todo en un cajón: estado de cliente y de servidor revueltos en el mismo store global. La disciplina que propone es partir por la propiedad, la pregunta que ha recorrido las cuatro lecciones: quién es dueño de la verdad.
El estado de cliente es el que posees: lo crea y lo muta tu aplicación, y tu última escritura es la verdad. El menú abierto, el paso del asistente, el tema oscuro. El estado de servidor es el que solo lees: su dueño vive en otra máquina y tu copia envejece sola. El perfil, el catálogo, los pedidos. Dos clases con dos naturalezas, y por tanto dos herramientas.
Cliente: lo posees
Menú abierto, paso del asistente, tema oscuro. Tu escritura es la verdad. Vive en useState, Zustand o Jotai.
Servidor: solo lo lees
Perfil, catálogo, pedidos. Su dueño vive en otra máquina y tu copia envejece. Vive en TanStack Query o SWR.
URL: estado navegable
Filtros, pestaña activa, paginación. Compartible por enlace y restaurable al recargar. Vive en la URL.
Formulario: borrador efímero
Campos a medio escribir que se validan entre sí. Vive en una librería de formularios hasta que se envían.
Meter la segunda clase en la primera es el error de categoría del que partió el nivel. Y respetar la frontera es más sencillo de lo que parece: un componente puede leer de una cache y de un store a la vez sin mezclarlos, porque cada dato vive donde su naturaleza manda.
function Pantalla() {
// servidor: solo lo leemos, vive en una cache
const { data: pedidos } = useQuery({ queryKey: ["pedidos"], queryFn: traerPedidos });
// cliente: lo poseemos, vive en un store ligero
const temaOscuro = useUI((s) => s.temaOscuro);
return <Lista pedidos={pedidos} oscuro={temaOscuro} />;
}
flowchart TD E[estado de la app] --> C[cliente: lo que posees] E --> S[servidor: lo que solo lees] E --> U[url: estado navegable] E --> F[formularios: borrador efimero] C --> CT[useState Zustand Jotai] S --> ST[TanStack Query SWR] style C fill:#a6e3a1,color:#11111b style S fill:#fab387,color:#11111b style U fill:#89b4fa,color:#11111b style F fill:#cba6f7,color:#11111b
El error más repetido al montar este stack es traer una lista con la cache y, acto seguido, guardarla en el store para tenerla a mano. En ese instante el dato vive en dos sitios: la cache, que sabe cuándo caduca y cómo revalidar, y una copia en el store, que no sabe nada de eso y quedará obsoleta en cuanto la verdad remota cambie. Has recreado, a mano y peor, el anti-patrón que hundió a Redux. La regla no admite excepción cómoda: el dato de servidor vive en la cache y se lee de ella; si otro componente lo necesita, vuelve a leerlo con la misma clave, que deduplica, en vez de copiarlo al store.
Lo que queda cuando el servidor se va
La sorpresa de aplicar esta separación a una app real es cuánto encoge el store global. Cuando extraes cada dato que en verdad vino de una petición HTTP, lo que queda casi nunca justifica la maquinaria pesada que tenías montada.
Buena parte de lo que la gente guardaba en Redux nunca fue estado de cliente: era servidor de paso, y con él se iban también los reducers, thunks y acciones que solo existían para leerlo y sincronizarlo. Al sacarlo, no pierdes lógica de dominio; tiras una cache casera que nunca debió estar ahí.
Lo que sobrevive es estado de cliente genuino, y suele ser poco: preferencias de UI, selección actual, un asistente a medias, un modal abierto. Tan poco que a menudo un store ligero —Zustand, Jotai, o incluso useState elevado— basta y sobra. Esta es, medida por el ecosistema, una de las razones de la caída de Redux entre 2020 y 2026: no es que empeorara, es que le quitaron el trabajo que en realidad nunca le correspondió.
El patrón se ve mejor con números que con adjetivos. Una app de comercio típica guardaba en el store el catálogo, el carrito remoto, los pedidos, el perfil y media docena de listas: todo servidor. Al migrarlo a una cache, el store baja de decenas de slices a dos o tres piezas de cliente puro. No es que Redux fuera pesado; es que cargaba con equipaje ajeno.
Menos estado global, menos bugs
La promesa menos bugs no es un eslogan, tiene un mecanismo. Los bugs más difíciles del estado global son de sincronización: dos copias del mismo dato que discrepan, una vista que muestra lo viejo porque nadie la avisó del cambio, un dato que quedó obsoleto sin que nada lo revalidara.
Y resulta que esa familia entera nace, casi siempre, de gestionar a mano una cache dentro de un store que no sabía que lo era. El bug no estaba en tu lógica; estaba en haber puesto un dato prestado donde solo caben los propios.
Cuando entregas el estado de servidor a una cache de verdad, esos bugs no se arreglan: dejan de ser posibles. La deduplicación, la revalidación al reenfocar y la invalidación tras escribir dejan de ser código tuyo que puede fallar y pasan a ser garantías de la librería. Reduces la superficie de estado global mutable —la fuente número uno de bugs de estado— y con ella desaparece la clase de errores que vivía en esa superficie.
Para una app web nueva hoy: TanStack Query, o los loaders del framework más SWR, para el estado de servidor, que ya no vive en el store; un store ligero como Zustand o Jotai, o simplemente useState, para el poco estado de cliente que quede; la URL como fuente de verdad de lo navegable —filtros, pestaña activa, paginación—; y una librería de formularios para el borrador efímero. Redux Toolkit sigue siendo una opción legítima para el estado de cliente complejo y con historia, pero rara vez para datos de servidor. Cada clase, su herramienta.
El servidor que ni siquiera cruza
Hay un giro final que empuja el patrón aún más lejos: los componentes de servidor. Con los React Server Components y el hook use, buena parte del estado de servidor ni siquiera cruza al cliente como estado; se resuelve en el servidor y llega ya renderizado, o se pasa como promesa que el cliente consume con Suspense.
El estado de cliente encoge todavía más, porque datos que antes había que traer, cachear y pintar en el navegador ahora nunca salen del servidor. El modelo mental no cambia —sigue siendo una cache de una verdad remota—, solo se mueve dónde vive la cache. La dirección del viaje es inequívoca: cada año, menos estado en el cliente.
// Componente de servidor: el dato se resuelve aqui y nunca cruza como estado
async function Pedidos() {
const pedidos = await traerPedidos(); // ocurre en el servidor
return <Lista pedidos={pedidos} />;
}
Separar servidor de cliente es el primer patrón transversal del bloque, no el único. Los niveles siguientes recorren los demás miembros de la taxonomía con la misma lógica de dueño: la URL como fuente de verdad de lo navegable, los formularios como borrador efímero, la actualización optimista como reconciliación adelantada, y la persistencia con el enfoque local-first para cuando el cliente también debe sobrevivir sin red. Todos responden, en el fondo, a la misma pregunta de este nivel: de quién es esta verdad y dónde debe vivir.
La lección que corona el nivel no es una librería ni una API, es una forma de clasificar, y por eso vale más que cualquier herramienta que sobreviva a esta década. Durante años la pregunta de arquitectura de estado fue qué gestor de estado uso, y era la pregunta equivocada, porque presuponía que el estado era una cosa homogénea que un solo gestor debía gobernar. El patrón dominante de 2026 nace de rechazar esa premisa: el estado no es una cosa, son varias clases con naturalezas distintas, y la primera decisión, antes de elegir ninguna librería, es clasificar cada pieza por su propiedad. Soy dueño de esta verdad o solo la leo. Si la posees, es estado de cliente y va a un store. Si solo la lees porque su dueño vive en un servidor, es una cache y va a una librería de cache. Si describe dónde está el usuario en la app, pertenece a la URL. Si es un borrador a medio escribir, es de un formulario. Esa clasificación, hecha antes que nada, es lo que hace que el resto de decisiones se vuelvan fáciles y que categorías enteras de bugs no lleguen a existir, porque cada clase acaba en la herramienta diseñada para su naturaleza en vez de en un cajón único que finge que todo el estado es igual. Menos estado global, menos bugs es la consecuencia medible de una causa conceptual: dejaste de tratar como propio lo que era prestado, y al hacerlo le quitaste al store global la mitad de su contenido y, con esa mitad, la mayoría de sus fallos de sincronización. Las librerías que hoy encarnan este patrón cambiarán de nombre; la taxonomía que lo sostiene, clasificar el estado por quién es dueño de su verdad, es de las pocas ideas de esta disciplina que puedes apostar a que seguirán en pie cuando ninguna de estas herramientas exista ya.
- Toma una pantalla real y clasifica cada pieza de estado en cuatro cubos: cliente, servidor, URL y formulario.
- Extrae del store global un dato que en realidad venía del servidor y dáselo a una cache; mide cuántas líneas de sincronización mueren con él.
- Cuenta lo que queda en tu store tras la extracción y decide con honestidad si aún necesitas la maquinaria pesada que tenías.
- Identifica un bug de sincronización que hayas sufrido y explica por qué no sería posible con el servidor en una cache.
- Escribe el stack que elegirías hoy para una app nueva, una herramienta por clase de estado, y justifica cada elección por la naturaleza de la clase.
- Formula la pregunta que precede a toda decisión de estado: soy dueño de esta verdad o solo la leo.