Server Actions de Next.js 16 en 2026: mutaciones, revalidación y preguntas de entrevista
Cómo las Server Actions de Next.js 16 manejan mutaciones, revalidación, estado pendiente, UI optimista y seguridad, con las preguntas de entrevista que evalúan cada concepto.

Las Server Actions de Next.js convierten una simple función asíncrona en un endpoint de mutación del lado del servidor que un formulario puede invocar directamente, sin ruta de API, sin fetch del cliente y sin serialización manual de JSON. En Next.js 16 son la forma predeterminada de escribir mutaciones de datos y aparecen en casi toda entrevista senior de React en 2026. Este análisis a fondo desglosa cómo se ejecutan, cómo la revalidación propaga datos frescos a la interfaz, cómo modelar los estados pendiente y de error, y las reglas de seguridad que hacen tropezar a la mayoría de los desarrolladores.
Una Server Action es una función asíncrona marcada con "use server" que se ejecuta solo en el servidor y puede invocarse desde un componente de cliente o un formulario como si fuera local, mientras Next.js gestiona automáticamente la petición de red, la serialización de argumentos y la protección CSRF.
Cómo se ejecutan las Server Actions internamente
Una Server Action es una función exclusiva del servidor que se invoca por la red a través de un endpoint generado por el compilador. Cuando una función lleva la directiva "use server", el compilador de Next.js nunca envía su cuerpo al navegador. En su lugar, reemplaza la función por una referencia ligera: un ID de acción con hash asociado a una ruta POST generada. Al invocar la acción desde el cliente se envía una petición a esa ruta, la función real se ejecuta en el servidor y el resultado serializado regresa en streaming a quien la llamó.
Este diseño tiene tres consecuencias que conviene recordar tanto para el código de producción como para las entrevistas. Primero, los argumentos y los valores de retorno deben ser serializables, porque cruzan la red como cargas útiles codificadas. Segundo, los secretos permanecen en el servidor, ya que el cuerpo de la función nunca se empaqueta para el cliente. Tercero, las acciones asociadas a un <form> funcionan incluso antes de la hidratación, razón por la cual la mejora progresiva es uno de sus principales atractivos. La guía oficial de Next.js sobre actualización de datos documenta el modelo de ejecución completo.
Toda Server Action reside en un archivo o bloque marcado con "use server". Una mutación simple luce así:
"use server"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
import { PostService } from "@/lib/services/post-service"
export async function createPost(formData: FormData) {
// FormData values arrive as strings; cast explicitly
const title = formData.get("title") as string
const body = formData.get("body") as string
// Run business logic through the service layer, never the ORM directly
const post = await PostService.create({ title, body })
// Purge the cached list so the next request refetches it
revalidatePath("/posts")
// Send the user to the freshly created resource
redirect(`/posts/${post.id}`)
}La función lee el FormData enviado, persiste a través de un servicio, invalida el listado cacheado y redirige. No se requiere nada de JavaScript en el cliente para lograrlo.
Conectar una Server Action a un formulario sin JavaScript en el cliente
Pasar la acción a la prop action de un formulario es la integración más simple. Next.js serializa el envío en FormData e invoca la función en el servidor. Como esto usa el mecanismo nativo de envío de formularios, funciona con JavaScript deshabilitado y mejora sin interrupciones una vez que la página se hidrata.
import { createPost } from "./actions"
export default function NewPostForm() {
return (
<form action={createPost}>
<input name="title" required />
<textarea name="body" required />
<button type="submit">Publish</button>
</form>
)
}El atributo name de cada campo se convierte en la clave que lee formData.get(). La referencia de FormData en MDN explica cómo el navegador construye este objeto a partir del formulario. Para un control más amplio, next/form extiende el elemento nativo con navegación del lado del cliente y prefetching, manteniendo el mismo contrato basado en acciones.
Gestionar el estado pendiente y de error con useActionState
Los formularios reales necesitan retroalimentación de validación y un indicador de carga. El hook useActionState de React 19 envuelve una Server Action, propaga un valor de estado a través de cada envío y expone una bandera isPending. Sustituye al antiguo nombre useFormState y devuelve una tupla con el estado actual, una acción envuelta y el booleano de pendiente.
"use client"
import { useActionState } from "react"
import { createPost } from "./actions"
const initialState = { error: "" }
export default function NewPostForm() {
// state carries the last return value; isPending tracks the in-flight request
const [state, formAction, isPending] = useActionState(createPost, initialState)
return (
<form action={formAction}>
<input name="title" required />
<textarea name="body" required />
{state.error ? <p role="alert">{state.error}</p> : null}
<button type="submit" disabled={isPending}>
{isPending ? "Publishing..." : "Publish"}
</button>
</form>
)
}Cuando un hook maneja la acción, la firma de la función incorpora un primer parámetro prevState antes de FormData. Devolver un objeto en lugar de lanzar una excepción permite que el componente muestre la validación en línea sin necesidad de un error boundary:
"use server"
import { revalidateTag } from "next/cache"
import { PostService } from "@/lib/services/post-service"
type FormState = { error: string }
export async function createPost(
prevState: FormState,
formData: FormData,
): Promise<FormState> {
const title = formData.get("title") as string
// Validate on the server; client validation is never trustworthy alone
if (!title || title.length < 3) {
return { error: "Title must be at least 3 characters." }
}
await PostService.create({ title })
// Invalidate by tag rather than path for finer-grained control
revalidateTag("posts")
return { error: "" }
}La referencia de useActionState en react.dev cubre patrones avanzados como el soporte de permalinks para envíos previos a la hidratación. Devolver un estado estructurado mantiene el camino feliz y el camino de error en un mismo lugar predecible.
revalidatePath vs revalidateTag: elegir la invalidación correcta
Una mutación es solo la mitad del trabajo; la interfaz debe reflejarla. Next.js expone dos funciones de invalidación desde next/cache, y elegir la correcta es un discriminador frecuente en las entrevistas.
| Función | Invalida | Mejor cuando |
|----------|-------------|-----------|
| revalidatePath("/posts") | Toda entrada cacheada de una ruta | La mutación afecta a una página o layout claro |
| revalidateTag("posts") | Todo fetch cacheado etiquetado con ese tag | Los mismos datos aparecen en varias rutas |
revalidatePath es de grano grueso y centrada en rutas: limpia la caché de una URL y su árbol de layouts. revalidateTag es granular y centrada en datos: cualquier fetch o función cacheada etiquetada con la cadena se purga esté donde esté. Los tags escalan mejor en aplicaciones grandes porque una sola mutación puede refrescar una barra lateral, un listado y una página de detalle en una única llamada. Ambas funciones marcan los datos como obsoletos en lugar de volver a solicitarlos de inmediato, de modo que la siguiente petición regenera el contenido.
Con los Cache Components de Next.js 16, el etiquetado se integra con la directiva "use cache" mediante cacheTag, lo que cambia la forma de razonar sobre el cacheo en toda la aplicación. Esa interacción se aborda en profundidad en la guía de Cache Components de Next.js 16 en SharpSkill.
¿Listo para aprobar tus entrevistas de React / Next.js?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Actualizaciones optimistas de la UI con useOptimistic
Los viajes de ida y vuelta por la red añaden una latencia perceptible. El hook useOptimistic renderiza el resultado esperado al instante y luego lo reconcilia con la respuesta del servidor una vez que la acción se resuelve. Recibe el estado actual y un reducer que produce la versión optimista.
"use client"
import { useOptimistic } from "react"
import { likePost } from "./actions"
export function LikeButton({ postId, likes }: { postId: string; likes: number }) {
// optimisticLikes updates before the server confirms
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(current) => current + 1,
)
async function handleLike() {
addOptimisticLike(null) // update the UI immediately
await likePost(postId) // then run the real mutation
}
return (
<form action={handleLike}>
<button type="submit">Like ({optimisticLikes})</button>
</form>
)
}Si la acción falla, React revierte automáticamente el valor optimista al estado confirmado por el servidor, por lo que no hace falta un rollback manual. La referencia de useOptimistic en react.dev detalla cómo el ciclo de reconciliación interactúa con las transiciones. Este patrón es lo que hace que los formularios con Server Actions se sientan tan responsivos como una SPA renderizada por completo en el cliente.
Enlazar argumentos e invocar acciones desde manejadores de eventos
No toda mutación se origina en el envío de un formulario. Eliminar una fila, alternar una configuración o reordenar una lista suele dispararse desde el clic de un botón u otro evento. Una Server Action es una referencia de función normal en el cliente, así que puede invocarse desde cualquier manejador, siempre que la llamada se ejecute dentro de una transición para mantener la interfaz responsiva.
Los argumentos adicionales que no son campos del formulario se adjuntan con bind, que produce una nueva acción con esos valores antepuestos. Esta es la forma idiomática de pasar un ID junto con el FormData enviado.
"use client"
import { useTransition } from "react"
import { deletePost, updatePost } from "./actions"
export function PostActions({ postId }: { postId: string }) {
const [isPending, startTransition] = useTransition()
// Bind the id so the action receives it before FormData
const updateWithId = updatePost.bind(null, postId)
return (
<div>
<form action={updateWithId}>
<input name="title" />
<button type="submit">Save</button>
</form>
{/* Event-handler invocation wrapped in a transition */}
<button
onClick={() => startTransition(() => deletePost(postId))}
disabled={isPending}
>
{isPending ? "Deleting..." : "Delete"}
</button>
</div>
)
}Los argumentos enlazados se cifran como cualquier otro valor capturado por el closure, de modo que el postId no se manipula en tránsito, aunque la acción debe confirmar de todas formas que quien la invoca tiene permiso para actuar sobre ese registro. Envolver la llamada directa en startTransition permite que React mantenga la interfaz interactiva y exponga el estado pendiente sin necesidad de un formulario.
Seguridad de las Server Actions: trata cada acción como una API pública
El malentendido más peligroso es creer que una Server Action es privada porque se escribe junto al código del servidor. En realidad, una vez que una acción se utiliza, Next.js genera un endpoint POST público que cualquiera puede invocar con una petición manipulada. El framework proporciona IDs de acción seguros y elimina las acciones sin uso durante next build, pero no autoriza a quien la invoca.
Las Server Actions son endpoints HTTP accesibles. Las comprobaciones de sesión en un layout o en el middleware no las protegen, porque un atacante puede hacer un POST directamente a la acción. Es necesario verificar la sesión y los permisos de quien la invoca dentro del cuerpo de la acción antes de tocar cualquier dato.
"use server"
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { UserService } from "@/lib/services/user-service"
export async function deleteUser(userId: string) {
// Authenticate on every invocation, not in a wrapper component
const session = await auth.api.getSession({ headers: await headers() })
if (!session || session.user.role !== "admin") {
throw new Error("Unauthorized")
}
await UserService.delete(userId)
}Hay dos reglas más que importan. Los valores capturados por el closure de una acción en línea se cifran antes de enviarse al cliente y se descifran en el servidor, de modo que un token capturado no se filtra en texto plano, pero depender de ello es frágil: lo mejor es mantener los secretos completamente fuera de los closures. Y cualquier argumento que provenga del cliente es entrada no confiable que debe validarse, igual que el cuerpo de una petición REST. La guía de seguridad de datos de Next.js formaliza el modelo de taint que respalda estas garantías. Para practicar de forma estructurada estos compromisos, el módulo de entrevista sobre Server Actions de Next.js en SharpSkill repasa exactamente las preguntas que hacen los paneles de contratación.
Preguntas de entrevista sobre Server Actions de Next.js 16
Estas preguntas aparecen una y otra vez en los procesos senior de React y full-stack de 2026.
¿Por qué una Server Action solo puede aceptar y devolver valores serializables? Porque los argumentos y los resultados cruzan la frontera de red entre el cliente y el servidor como cargas útiles codificadas. Las funciones, las instancias de clase y los nodos del DOM no pueden serializarse, así que pasarlos lanza un error. FormData, los objetos planos, los arreglos y los primitivos son seguros.
¿Cómo funciona la mejora progresiva con las Server Actions? Cuando una acción se pasa directamente a la prop action de un formulario, el navegador envía el formulario de forma nativa mediante una petición POST incluso antes de que React se hidrate. Next.js intercepta y ejecuta la acción en el servidor, de modo que el formulario es funcional sin JavaScript en el cliente y evoluciona hacia una experiencia impulsada por el cliente tras la hidratación.
¿Cuál es la diferencia entre lanzar y devolver un error en una acción? Lanzar propaga el error al error boundary más cercano y es lo adecuado para fallos inesperados. Devolver un objeto de estado estructurado a través de useActionState es lo adecuado para errores de validación esperados que deben mostrarse en línea sin desmontar el formulario.
¿Llamar a revalidatePath vuelve a solicitar los datos de inmediato? No. Marca las entradas cacheadas como obsoletas para que la siguiente petición las regenere. La respuesta actual no se ve afectada, razón por la cual una redirección tras una mutación aterriza en contenido recién renderizado.
Para el contexto de renderizado más amplio dentro del cual se ejecutan estas mutaciones, el análisis de React 19 Server Components en producción complementa de forma natural el conocimiento de las Server Actions.
¿Listo para aprobar tus entrevistas de React / Next.js?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Conclusión
Las Server Actions de Next.js 16 consolidan las mutaciones, la revalidación y la mejora progresiva en una única primitiva orientada al servidor. Las conclusiones clave:
- Marcar las mutaciones con
"use server"y pasarlas a la propactionde un formulario para obtener gratis la gestión de red y la mejora progresiva. - Manejar el estado pendiente y de validación con
useActionState, devolviendo un objeto estructurado para los errores esperados en lugar de lanzar excepciones. - Recurrir a
revalidatePathcuando cambia una ruta y arevalidateTagcuando los mismos datos abarcan varias rutas. - Usar
useOptimisticpara renderizar el resultado esperado al instante y dejar que React reconcilie o revierta automáticamente. - Autorizar y validar dentro del cuerpo de cada acción, porque cada acción utilizada es un endpoint HTTP público que el middleware por sí solo no protege.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Etiquetas
Compartir
Artículos relacionados

Cache Components en Next.js 16 en 2026: use cache, PPR y Preguntas de Entrevista
Análisis profundo de Cache Components en Next.js 16: la directiva use cache, Partial Pre-Rendering (PPR), cacheLife, cacheTag, y preguntas de entrevista técnica para desarrolladores senior.

React Compiler en 2026: memoización automática y preguntas de entrevista
Análisis completo del React Compiler en 2026: pipeline de compilación, memoización automática, reglas de React y preguntas frecuentes en entrevistas técnicas.

Testing en React 2026: Vitest, React Testing Library y Mejores Prácticas
Dominar el testing en React con Vitest y React Testing Library. Aprender patrones de testing de componentes, manejo asíncrono, estrategias de mocking y mejores prácticas para entrevistas técnicas en 2026.