Elegir entre los ligeros: la tabla honesta
Zustand, Jotai, Valtio y nanostores resuelven el mismo problema con cuatro modelos mentales distintos, y la mayoría de las comparaciones que circulan miden lo que es fácil de medir: kilobytes y estrellas. Esta lección construye la tabla que sí decide —modelo mental, forma del estado que favorece, coste de aprendizaje, seguridad de tipos, dependencia de framework, herramientas de depuración y coste de migración— y luego enumera los criterios que ninguna tabla captura: el tamaño del equipo, la vida esperada del código y qué ya sabe quien va a mantenerlo.
Las cuatro librerías de este bloque son buenas, pequeñas y están bien mantenidas, lo que convierte la comparación en algo más difícil que un ranking. Cuando ninguna opción es mala, la pregunta deja de ser cuál es mejor y pasa a ser cuál encaja con la forma de tu estado, de tu equipo y de tu producto. La industria lleva años respondiéndola con las dos métricas que cualquiera puede publicar sin pensar —el peso en kilobytes y el número de descargas— precisamente porque son las únicas comparables sin conocer tu problema, y también las que menos deciden. Esta lección construye la tabla que sí importa, la lee criterio por criterio y termina admitiendo lo que ninguna tabla puede decidir por ti.
- Enunciar el modelo mental de
Zustand,Jotai,Valtioynanostoresen una frase cada uno. - Comparar las cuatro por criterios que decidan de verdad y no por peso ni popularidad.
- Reconocer los criterios organizativos que ninguna tabla técnica captura.
- Aplicar un procedimiento de elección que empiece por la forma del estado y no por la librería.
Cuatro modelos, cuatro frases
Antes de comparar hay que saber qué se compara, y lo que distingue a estas librerías no es la API sino la idea que cada una tiene de qué es el estado. Zustand dice que el estado es un objeto único que se sustituye por partes con una función; el modelo es Flux comprimido a un hook, sin acciones ni reductores. Jotai dice que el estado es un grafo de piezas mínimas que se componen y se recalculan solas; el modelo es la hoja de cálculo. Valtio dice que el estado es un objeto vivo que se muta y del que se sacan fotos; el modelo es la observación por intercepción. Nanostores dice que el estado es un valor con oyentes que vive en el módulo y no en ningún árbol; el modelo es el observable mínimo.
`Zustand` · el objeto único
Un store, selectores explicitos, actualizacion con set. Lo mas parecido a Redux sin su ceremonia, y el default razonable en React.
`Jotai` · el grafo
Atomos que se derivan unos de otros. Brilla cuando el estado es una red de dependencias y no un puñado de banderas.
`Valtio` · el objeto vivo
Mutacion directa y seguimiento por acceso. Gana en escritura imperativa y en estructuras profundamente anidadas.
`nanostores` · el observable
Sin framework, menos de un kilobyte, perezoso. La respuesta cuando hay varias raices de montaje o varios frameworks.
Vale la pena notar que tres de las cuatro salieron del mismo colectivo y comparten autoría, lo que explica por qué no compiten como productos rivales: son exploraciones deliberadas de tres modelos distintos, publicadas por gente que asumía que ninguna respuesta única sirve para todo el estado de todas las aplicaciones. Leerlas como competidoras es un malentendido; leerlas como un catálogo de formas es entenderlas.
La prueba más rápida de que los modelos difieren de verdad es escribir la misma operación en las cuatro y mirar qué cambia. No la sintaxis, que es anécdota, sino qué tienes que saber para escribirla:
// Zustand: describes como transformar el objeto unico
useStore.setState((s) => ({ contador: s.contador + 1 }))
// Jotai: escribes en un atomo y el grafo se recalcula solo
setContador((n) => n + 1)
// Valtio: mutas la verdad viva y el seguimiento decide quien mira
estado.contador += 1
// nanostores: sustituyes el valor del observable
contador.set(contador.get() + 1)
Las cuatro líneas hacen lo mismo y exigen conocimientos distintos. La primera pide saber dónde vive esa clave dentro del objeto único. La segunda pide saber qué otras piezas dependen de ese átomo. La tercera no pide saber nada al escribir y lo pide todo al depurar. La cuarta pide saber en qué módulo vive ese store y quién más lo escucha. Elegir librería es, en buena medida, elegir cuál de esos cuatro conocimientos quieres que tu equipo tenga presente a diario.
La tabla que sí decide
| Criterio | Zustand |
Jotai |
Valtio |
nanostores |
|---|---|---|---|---|
| Modelo mental | objeto único | grafo de átomos | objeto mutable observado | observable en módulo |
| Forma que favorece | valores planos compartidos | derivación interdependiente | anidamiento profundo e imperativo | estado entre raíces múltiples |
| Coste de aprendizaje | bajo | medio | bajo al usar, alto al depurar | muy bajo |
| Optimización del render | selector explícito | por átomo | automática por acceso | por store |
| Seguridad de tipos | alta | alta | media | alta salvo rutas de texto |
| Depende del framework | React | React | React | ninguno |
| Depuración | buena, con puente a devtools | media | limitada | manual |
| Coste de migrar desde Redux | bajo | medio | medio | alto |
Ninguna celda de esa tabla dice malo. Todas dicen distinto, y la única forma de convertirla en una decisión es ponderar las filas según tu caso antes de leerlas, no después.
Cada fila merece una lectura y no una ojeada. La de optimización del render es la que más divide: Zustand te obliga a escribir un selector y te castiga si lo olvidas con renders de más; Valtio no te obliga a nada y acierta solo; Jotai reparte la optimización en la granularidad de sus átomos; nanostores re-renderiza por store, lo que empuja a tener stores pequeños. La automatización de Valtio parece ganar hasta que se lee la fila siguiente.
La fila de seguridad de tipos también merece matiz, porque suele darse por empatada y no lo está. Zustand y Jotai tipan con precisión tanto la lectura como la escritura, y un error de nombre lo detecta el compilador. Valtio tipa el proxy y la instantánea, pero no puede tipar la disciplina: nada en el sistema de tipos distingue leer del proxy en el render de leerlo en un manejador, que es justo la frontera donde se rompen las cosas. Nanostores tipa bien salvo en las rutas por cadena de texto de deepMap, donde el error viaja hasta el tiempo de ejecución. Ninguna de estas grietas es grave por sí sola, pero conviene saber cuáles de tus errores atrapará el compilador y cuáles llegarán a producción.
Porque la fila de depuración invierte parte del resultado. Un selector explícito es código visible, con nombre, que se puede leer en una revisión y probar por separado; un cuaderno de accesos es un hecho de tiempo de ejecución que nadie ve. Cuando un componente de Zustand se re-renderiza de más, la causa está escrita en el selector; cuando uno de Valtio no se actualiza, hay que reconstruir mentalmente qué leyó el render. La explicitud cuesta al escribir y ahorra al diagnosticar, y qué prefieras depende de cuántas veces vas a escribir ese código frente a cuántas vas a depurarlo.
Las cuatro pesan entre menos de uno y unos cuatro kilobytes comprimidos. La diferencia entre la más ligera y la más pesada es inferior a lo que ocupa una tipografía o un icono en tu página, y salvo que estés en un sitio de contenido optimizado al extremo, es ruido frente al coste real de una elección equivocada: el tiempo que un equipo pierde peleando con un modelo mental que no encaja con su problema. Usa el peso como criterio de desempate cuando el resto ya empató, jamás como criterio principal. Y desconfía de cualquier comparación que lo ponga en la primera columna.
Los criterios que la tabla no captura
Hay tres factores que deciden más que cualquier fila técnica y que ninguna comparación publicada puede conocer. El primero es qué sabe ya tu equipo. Una librería que la mitad de la gente entiende sin esfuerzo produce mejor código que otra técnicamente superior que nadie domina, porque el código lo escriben y lo mantienen personas concretas con un conocimiento concreto. Si vienes de Redux, Zustand es casi continuidad; si vienes de Vue o de Svelte, el modelo de Valtio y el de nanostores te resultarán más familiares que el de los átomos.
El segundo es la vida esperada del código. Un prototipo que morirá en seis semanas debe optimizar velocidad de escritura, y ahí la ausencia de ceremonia gana sin discusión. Un sistema que vivirá ocho años y pasará por cuatro equipos debe optimizar legibilidad para quien llegue sin contexto, y ahí la explicitud vale lo que cuesta. Es el mismo eje que separaba a Redux de la ola ligera, aplicado ahora dentro de la propia ola.
El tercero es la heterogeneidad del entorno. Si hay una sola aplicación de React, tres de las cuatro opciones son viables y la cuarta no aporta. Si hay islas, micro frontends o una migración gradual entre frameworks conviviendo, la fila de dependencia de framework deja de ser una fila más y se convierte en un filtro que elimina candidatas antes de mirar el resto.
Hay un cuarto factor que casi nunca se nombra y que decide más de lo que parece: cuántas personas van a tocar ese estado sin haberlo escrito. Un modelo mental implícito, como el seguimiento por acceso de Valtio, funciona espléndidamente mientras quien lo mantiene lo entiende, y se degrada rápido cuando entra gente que no lo ha estudiado, porque los síntomas de usarlo mal son mudos. Un modelo explícito, como el selector de Zustand, es más lento de escribir pero se comunica solo, y esa propiedad importa exactamente en proporción a cuánta rotación tenga tu equipo. La elección técnica es también una apuesta sobre cuánta transmisión de conocimiento puedes garantizar.
Que una librería tenga diez veces más descargas que otra dice algo real: habrá más respuestas cuando te atasques, más integraciones ya escritas y más probabilidad de que el siguiente contratado la conozca. Eso es valor auténtico y sería tonto ignorarlo. Lo que no dice es que encaje con la forma de tu estado, y confundir ambas cosas produce el patrón más común de mala elección técnica: adoptar lo popular, forzar el problema a caber en ello y concluir que el problema era raro. Trata la popularidad como lo que es, un desempate con peso real entre opciones que ya encajan, no como el primer criterio.
flowchart TD
A[una pieza de estado del cliente] --> B{varias raices o varios frameworks?}
B -->|si| C[nanostores]
B -->|no| D{es un grafo de derivaciones?}
D -->|si| E[Jotai]
D -->|no| F{la escritura es imperativa y anidada?}
F -->|si| G[Valtio]
F -->|no| H[Zustand]
style C fill:#cdd6f4,color:#11111b
style E fill:#89b4fa,color:#11111b
style G fill:#f9e2af,color:#11111b
style H fill:#a6e3a1,color:#11111bNada impide usar dos de estas librerías en la misma aplicación, y a veces es lo correcto: nanostores para lo que cruza islas y Zustand dentro de una aplicación grande, por ejemplo. La frontera entre lo sano y lo caótico es si puedes enunciar en una frase qué clase de estado vive en cada una. Si puedes, tienes una arquitectura con dos herramientas bien delimitadas. Si no puedes, tienes dos maneras de hacer lo mismo y la próxima persona elegirá al azar, que es exactamente cómo empiezan las bases de código donde nadie sabe dónde buscar un dato.
El procedimiento
La elección deja de ser una cuestión de gusto si se hace en el orden correcto, y el orden correcto empieza lejos de los nombres. Primero, aparta lo que no es estado del cliente: la cache del servidor va a TanStack Query y lo navegable va a la URL, decisiones que ninguna de estas cuatro discute. Segundo, describe la forma de lo que queda: plano o grafo, superficial o anidado, un árbol o varios. Tercero, aplica el filtro del entorno, que puede eliminar candidatas de golpe. Cuarto, y solo entonces, desempata con lo que el equipo ya sabe y con la vida esperada del código.
El orden importa más que los criterios, y por una razón concreta: invertirlo produce un sesgo del que casi nadie escapa. Quien empieza por la librería que le gusta encuentra siempre argumentos para justificarla, porque las cuatro son defendibles y ninguna comparación honesta las descarta de plano. Quien empieza por la forma del estado llega a la librería casi sin margen de opinión, y descubre a menudo que la respuesta no era la que esperaba. Ese es el mismo movimiento que el árbol de decisión del Nivel 7 imponía a un piso de abstracción por encima: mirar el problema antes que el catálogo.
Conviene además fijar cuándo se revisa la decisión, porque una elección sin condiciones de revocación se convierte en dogma. Tres señales justifican volver a abrir el debate: que aparezca una segunda raíz de montaje o un segundo framework, que el estado plano empiece a llenarse de valores derivados unos de otros, o que la depuración de renders se lleve más tiempo que escribirlos. Ninguna de las tres es una emergencia, pero cada una indica que la forma del problema se movió y que la herramienta mínima de hoy puede no ser la de ayer.
La forma práctica de blindar esa reversibilidad es una disciplina de una línea que no depende de ninguna de las cuatro: el estado y sus transiciones viven en módulos de dominio que no importan nada de la vista, y la vista solo consume una superficie estrecha.
// dominio/carrito.ts · ninguna importacion de framework
export type Linea = { id: string; cantidad: number }
export function agregar(lineas: Linea[], id: string): Linea[] { /* ... */ }
export function total(lineas: Linea[], iva: number): number { /* ... */ }
Con las reglas del dominio fuera, la librería elegida queda reducida a un mecanismo de notificación de unas pocas líneas, y cambiarla afecta a ese envoltorio y no a la lógica. Es la misma tesis que atraviesa el track entero: la idea perdura, la encarnación caduca, y quien las mantiene separadas paga la caducidad al precio de una tarde.
Un apunte final sobre reversibilidad, porque reduce el peso emocional de la decisión. Estas cuatro librerías comparten una virtud que Redux no tenía: su superficie es tan pequeña que migrar entre ellas es un trabajo acotado y mecánico, casi siempre de horas y no de semanas, sobre todo si mantuviste el estado en módulos de dominio sin importaciones de vista. Elegir mal aquí no es una condena; es una corrección barata. Lo que sí resulta caro es no haber separado el dominio de la vista, porque entonces cualquier cambio de librería se convierte en tocar cien componentes.
Casi toda la literatura comparativa de este dominio comparte un vicio: enumera las virtudes de cada opción y omite sus renuncias, con lo que produce cuatro listas de ventajas entre las que es imposible elegir racionalmente. La comparación útil hace lo contrario y empieza por las pérdidas, porque una decisión de ingeniería es siempre una elección de qué estás dispuesto a perder. Zustand pierde ergonomía: te obliga a escribir selectores y a reconstruir objetos anidados a mano, y a cambio te da un código donde las dependencias están escritas y cualquiera puede leerlas. Jotai pierde inmediatez: te exige pensar tu estado como un grafo antes de escribir la primera línea, y a cambio te da recomputación automática y precisa cuando ese grafo existe de verdad. Valtio pierde explicitud: ninguna herramienta te dirá qué depende de qué, y a cambio te da la escritura más natural que el lenguaje permite y una optimización que nunca se olvida. Nanostores pierde ecosistema: casi todo lo que en las otras es un paquete maduro aquí lo escribes tú, y a cambio te da independencia total del framework y un coste que tiende a cero. Enunciadas así, las cuatro opciones dejan de ser competidoras y se revelan como cuatro puntos de un mismo espacio de compromisos, y la pregunta pasa a ser cuál de esas cuatro pérdidas puedes permitirte en tu contexto concreto. Esta forma de razonar no es específica del estado en el frontend; es lo que distingue una decisión técnica de una preferencia. Quien solo sabe recitar las ventajas de su herramienta favorita no ha elegido nada, ha adoptado algo, y la diferencia se hace visible el día que el contexto cambia y su herramienta deja de encajar sin que él sepa decir por qué. Si de estas cinco lecciones te llevas una sola destreza, que sea la de exigir a cada tecnología que te diga qué pierdes al adoptarla, y desconfiar profesionalmente de toda respuesta que sostenga que no pierdes nada.
- Inventaría el estado de cliente de un módulo real y descarta primero lo que sea cache de servidor o dato navegable. Trabaja solo con lo que quede.
- Clasifica cada pieza por forma: plana, grafo derivado o anidada e imperativa. Anota cuántas caen en cada categoría, porque la mayoría dominante orienta la elección.
- Aplica el filtro del entorno: cuenta cuántas raíces de montaje y cuántos frameworks conviven hoy en tu producto y cuántos convivirán en un año.
- Reescribe la tabla de la lección con solo las filas que a ti te importan y pondéralas. Si alguna fila no cambia tu decisión, elimínala.
- Para tu opción ganadora, escribe en una frase qué pierdes al elegirla. Si no encuentras la pérdida, no has entendido la opción todavía.
- Prototipa la misma pieza de estado en dos de las cuatro y mide lo único comparable de verdad: cuántas líneas ocupa, cuánto tarda alguien ajeno en entenderla y cuánto costaría cambiarla de librería.