revalidateTag vs updateTag vs revalidatePath en Next.js 16
Cuatro funciones de invalidación de cache, cada una con un caso de uso distinto. Aquí la matriz de decisión que no vas a encontrar en un solo lugar de la documentación.
Actualizas un dato desde una Server Action y necesitas que el cambio se refleje en la UI. Next.js te da no una, sino cuatro funciones distintas para esto: revalidateTag, updateTag, revalidatePath, y refresh(). La documentación las explica cada una por separado — pero nadie te dice, en una sola tabla, cuál usar según lo que tu usuario espera ver.
Eso es lo que vamos a resolver acá, con el caso real que me obligó a entenderlo bien: un checkout de e-commerce que descuenta stock, ya cubierto en detalle en mi artículo sobre los 3 mecanismos de cache en Next.js.
El cambio que rompe la mitad de los tutoriales viejos
Si aprendiste revalidateTag en un tutorial de hace más de unos meses, esto te va a sorprender: en Next.js 16, revalidateTag(tag) con un solo argumento está deprecado y produce error de TypeScript.
// ❌ Ya no compila en Next.js 16
revalidateTag('products')
// ✅ Ahora requiere un segundo argumento: el "profile" de cache
revalidateTag('products', 'max')
Ese segundo argumento no es un capricho de API — representa un concepto nuevo: cuánto tiempo puede servirse contenido viejo mientras se trae el fresco en segundo plano (stale-while-revalidate). 'max' es el valor recomendado para la mayoría de casos.
Junto con este cambio, apareció una función nueva: updateTag(), pensada para el caso opuesto — expiración inmediata, sin tolerar ni un momento de dato viejo.
La matriz de decisión
| Función | Cuándo usarla | Velocidad de reflejo | Dónde funciona |
|---|---|---|---|
updateTag(tag) | El usuario acaba de hacer algo y debe ver el resultado ya | Inmediato | Solo Server Actions |
revalidateTag(tag, 'max') | Contenido que tolera un poco de desactualización mientras se regenera en segundo plano | Stale-while-revalidate | Server Actions y Route Handlers |
revalidatePath(path) | Necesitas invalidar una ruta específica, sin depender de tags | Inmediato para esa ruta | Server Actions y Route Handlers |
refresh() | Solo quieres refrescar el árbol del cliente sin tocar el Data Cache del servidor | Inmediato, del lado del cliente | Server Actions |
La pregunta que realmente deberías hacerte antes de elegir es: ¿qué espera el usuario que acaba de generar este cambio?
Caso 1: acción directa del usuario → updateTag
Si alguien acaba de completar una compra, actualizar su perfil, o marcar algo como leído, la expectativa es clara: quiere ver su cambio reflejado ahora, no “en algún momento cercano”.
'use server'
import { updateTag } from 'next/cache'
import { prisma } from '@/lib/prisma'
export async function submitCheckout(cartId: string, /* ...datos del form */) {
// ...crea la orden, descuenta stock...
await prisma.$transaction(async (tx) => {
// lógica de orden + descuento de stock
})
// El cliente compró, el stock bajó: debe verse reflejado YA
updateTag('products')
}
Este es justo el caso del checkout: si el cliente que acaba de comprar vuelve a ver el producto, debe ver el stock actualizado, no una versión potencialmente vieja mientras se revalida sola.
Caso 2: contenido que tolera un pequeño delay → revalidateTag
Piensa en un blog, o un catálogo que cambia poco. Si un admin edita la descripción de un artículo, no pasa nada grave si el primer visitante después del cambio ve la versión anterior por una fracción de segundo mientras Next.js trae la fresca en segundo plano.
'use server'
import { revalidateTag } from 'next/cache'
export async function publishBlogPost(postId: string) {
await prisma.post.update({ where: { id: postId }, data: { published: true } })
revalidateTag('blog-posts', 'max') // stale-while-revalidate
}
La ventaja de este enfoque es que nunca bloquea al usuario que visita esa ruta justo después del cambio — recibe contenido inmediatamente (aunque sea el viejo), mientras el servidor regenera en background para la siguiente visita.
Caso 3: necesitas invalidar por ruta, no por tag → revalidatePath
A veces no tiene sentido diseñar un sistema de tags para una sola ruta muy específica que no comparte datos con nada más — por ejemplo, la propia página /carrito después de una mutación.
'use server'
import { revalidatePath } from 'next/cache'
export async function addToCart(productId: string, quantity: number) {
// ...lógica de agregar al carrito...
revalidatePath('/carrito')
}
revalidatePath no necesita que hayas etiquetado nada de antemano con tags — apunta directo a la ruta.
Caso 4: solo necesitas refrescar el cliente → refresh()
Esta es la menos conocida, y resuelve un caso puntual: cuando el cambio en sí no requiere invalidar ningún Data Cache del servidor, pero sí necesitas que el árbol de Server Components en el cliente se vuelva a pedir — por ejemplo, un contador que depende de datos ya frescos, pero el componente que lo muestra no se ha vuelto a renderizar.
'use server'
import { refresh } from 'next/cache'
export async function markNotificationAsRead(notificationId: string) {
await prisma.notification.update({ where: { id: notificationId }, data: { read: true } })
refresh() // refresca el router del cliente, sin invalidar Data Cache
}
El error más común: usar revalidateTag donde tocaba updateTag
El síntoma es siempre el mismo: “hice la acción, y aunque técnicamente invalidé el cache, la primera vez que vuelvo a ver la página sigo viendo el dato viejo”. Si tu invalidación usa revalidateTag(tag, 'max') para una acción donde el usuario espera ver el cambio de inmediato (como un checkout), ese es exactamente el comportamiento — no es un bug, es la función equivocada para la expectativa del usuario.
Conclusión
No existe una sola “función correcta” de invalidación — existen cuatro herramientas para cuatro expectativas distintas del usuario. La pregunta que debe guiar tu elección no es técnica, es de producto: ¿esta persona necesita ver su cambio ya, o puede tolerar un instante de contenido viejo mientras se regenera? Responde eso primero, y la función correcta se vuelve obvia.
¿Necesitas ayuda diseñando la estrategia de invalidación de cache de tu aplicación Next.js en producción? Agenda una asesoría técnica gratuita de 15 minutos.
¿Tienes un proyecto en mente?
Convierte tu idea en un producto real
Desarrollo web, aplicaciones a medida y consultoría tecnológica para empresas y startups. Cuéntame tu proyecto y te respondo en menos de 24 horas.