Server Actions no Next.js 16 em 2026: Mutações, Revalidação e Perguntas de Entrevista
Como as Server Actions do Next.js 16 lidam com mutações, revalidação, estado de pendência, UI otimista e segurança, com as perguntas de entrevista que testam cada conceito.

As Server Actions do Next.js transformam uma simples função assíncrona em um endpoint de mutação no servidor que um formulário pode chamar diretamente, sem rota de API, sem fetch no cliente e sem serialização manual de JSON. No Next.js 16, elas são a forma padrão de escrever mutações de dados e aparecem em quase toda entrevista sênior de React em 2026. Esta análise detalhada mostra como elas são executadas, como a revalidação propaga dados atualizados para a UI, como modelar estados de pendência e erro e as regras de segurança que derrubam a maioria dos desenvolvedores.
Uma Server Action é uma função assíncrona marcada com "use server" que roda apenas no servidor e pode ser chamada de um componente cliente ou de um formulário como se fosse local, enquanto o Next.js cuida automaticamente da requisição de rede, da serialização dos argumentos e da proteção contra CSRF.
Como as Server Actions são executadas por baixo dos panos
Uma Server Action é uma função exclusiva do servidor invocada pela rede por meio de um endpoint gerado pelo compilador. Quando uma função carrega a diretiva "use server", o compilador do Next.js nunca envia o corpo dela para o navegador. Em vez disso, ele substitui a função por uma referência leve: um ID de ação em hash mapeado para uma rota POST gerada. Chamar a ação a partir do cliente envia uma requisição para essa rota, a função real roda no servidor e o resultado serializado retorna em streaming para quem a chamou.
Esse design tem três consequências que vale a pena guardar tanto para o código de produção quanto para entrevistas. Primeira: argumentos e valores de retorno precisam ser serializáveis, porque atravessam a rede como payloads codificados. Segunda: segredos permanecem no servidor, já que o corpo da função nunca é empacotado para o cliente. Terceira: ações vinculadas a um <form> funcionam mesmo antes da hidratação, e é por isso que o aprimoramento progressivo é um dos grandes atrativos. O guia oficial do Next.js sobre atualização de dados documenta o modelo de execução completo.
Toda Server Action vive em um arquivo ou bloco marcado com "use server". Uma mutação simples se parece com isto:
"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}`)
}A função lê o FormData enviado, persiste os dados por meio de um service, invalida a listagem em cache e redireciona. Nenhum JavaScript no cliente é necessário para nada disso.
Conectando uma Server Action a um formulário sem JavaScript no cliente
Passar a ação para a prop action do formulário é a integração mais simples. O Next.js serializa o envio em FormData e chama a função no servidor. Como isso usa o mecanismo nativo de submissão de formulários, funciona com o JavaScript desativado e evolui de forma transparente assim que a página é hidratada.
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>
)
}O atributo name de cada campo se torna a chave lida por formData.get(). A referência de FormData na MDN explica como o navegador monta esse objeto a partir do formulário. Para um controle mais rico, o next/form estende o elemento nativo com navegação no cliente e prefetching, mantendo o mesmo contrato baseado em ações.
Gerenciando estados de pendência e erro com useActionState
Formulários reais precisam de feedback de validação e de um indicador de carregamento. O hook useActionState do React 19 envolve uma Server Action, encadeia um valor de estado a cada envio e expõe uma flag isPending. Ele substitui o antigo nome useFormState e retorna uma tupla com o estado atual, uma ação encapsulada e o booleano de pendência.
"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>
)
}Quando um hook comanda a ação, a assinatura da função ganha um primeiro parâmetro prevState antes de FormData. Retornar um objeto em vez de lançar uma exceção permite que o componente renderize a validação inline sem um 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: "" }
}A referência de useActionState no react.dev cobre padrões avançados, como suporte a permalink para envios anteriores à hidratação. Retornar um estado estruturado mantém o caminho de sucesso e o caminho de erro em um único lugar previsível.
revalidatePath vs revalidateTag: escolhendo a invalidação certa
Uma mutação é apenas metade do trabalho; a UI precisa refleti-la. O Next.js expõe duas funções de invalidação em next/cache, e escolher a correta é um critério frequente de diferenciação em entrevistas.
| Função | Invalida | Melhor quando |
|----------|-------------|-----------|
| revalidatePath("/posts") | Toda entrada em cache de uma rota | A mutação afeta uma página ou layout específico |
| revalidateTag("posts") | Todo fetch em cache rotulado com essa tag | Os mesmos dados aparecem em várias rotas |
revalidatePath é abrangente e centrado em rotas: limpa o cache de uma URL e de sua árvore de layouts. revalidateTag é granular e centrado nos dados: qualquer fetch ou função em cache rotulada com a string é purgado onde quer que esteja. Tags escalam melhor em aplicações grandes porque uma única mutação pode atualizar uma barra lateral, uma listagem e uma página de detalhe em uma só chamada. Ambas as funções marcam os dados como obsoletos em vez de refazer a busca imediatamente, de modo que a próxima requisição regenera o conteúdo.
Com os Cache Components do Next.js 16, a rotulagem se integra à diretiva "use cache" por meio de cacheTag, o que muda a forma de raciocinar sobre cache em toda a aplicação. Essa interação é detalhada no guia de Cache Components do Next.js 16 da SharpSkill.
Pronto para mandar bem nas entrevistas de React / Next.js?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Atualizações otimistas de UI com useOptimistic
As idas e voltas pela rede adicionam latência perceptível. O hook useOptimistic renderiza o resultado esperado instantaneamente e depois reconcilia com a resposta do servidor assim que a ação é resolvida. Ele recebe o estado atual e um reducer que produz a versão otimista.
"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>
)
}Se a ação falhar, o React automaticamente reverte o valor otimista para o estado confirmado pelo servidor, portanto nenhum rollback manual é necessário. A referência de useOptimistic no react.dev detalha como o ciclo de reconciliação interage com as transitions. É esse padrão que faz os formulários com Server Actions parecerem tão responsivos quanto uma SPA totalmente renderizada no cliente.
Vinculando argumentos e chamando ações a partir de manipuladores de eventos
Nem toda mutação nasce do envio de um formulário. Excluir uma linha, alternar uma configuração ou reordenar uma lista frequentemente parte de um clique de botão ou de outro evento. Uma Server Action é uma referência de função comum no cliente, então pode ser chamada de qualquer manipulador, desde que a chamada rode dentro de uma transition para manter a UI responsiva.
Argumentos adicionais que não são campos do formulário são anexados com bind, que produz uma nova ação com esses valores adicionados no início. Essa é a forma idiomática de passar um ID junto com o 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>
)
}Argumentos vinculados são criptografados como qualquer outro valor capturado por closure, então o postId não é adulterado em trânsito, embora a ação ainda precise confirmar que quem a chamou tem permissão para agir sobre aquele registro. Envolver a chamada direta em startTransition permite que o React mantenha a interface interativa e exponha o estado de pendência sem precisar de um formulário.
Segurança de Server Actions: trate toda ação como uma API pública
O equívoco mais perigoso é achar que uma Server Action é privada por estar escrita ao lado do código do servidor. Na prática, assim que uma ação é utilizada, o Next.js gera um endpoint POST público que qualquer pessoa pode chamar com uma requisição forjada. O framework fornece IDs de ação seguros e elimina ações não utilizadas durante o next build, mas não autoriza quem faz a chamada.
Server Actions são endpoints HTTP acessíveis. Verificações de sessão em um layout ou middleware não as protegem, porque um atacante pode enviar um POST diretamente para a ação. Verifique a sessão e as permissões de quem chama dentro do corpo da ação antes de tocar em qualquer dado.
"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)
}Mais duas regras importam. Valores capturados por closure em uma ação inline são criptografados antes de serem enviados ao cliente e descriptografados no servidor, então um token capturado não vaza em texto puro, mas depender disso é frágil: mantenha segredos totalmente fora das closures. E qualquer argumento vindo do cliente é uma entrada não confiável que precisa ser validada, exatamente como o corpo de uma requisição REST. O guia de segurança de dados do Next.js formaliza o modelo de taint por trás dessas garantias. Para praticar de forma estruturada esses trade-offs, o módulo de entrevista sobre Server Actions do Next.js da SharpSkill treina exatamente as perguntas que as bancas de contratação fazem.
Perguntas de entrevista sobre Server Actions no Next.js 16
Essas perguntas aparecem repetidamente nos processos seletivos sênior de React e full-stack em 2026.
Por que uma Server Action só pode aceitar e retornar valores serializáveis? Porque argumentos e resultados atravessam a fronteira de rede entre cliente e servidor como payloads codificados. Funções, instâncias de classe e nós do DOM não podem ser serializados, então passá-los lança um erro. FormData, objetos simples, arrays e primitivos são seguros.
Como o aprimoramento progressivo funciona com Server Actions? Quando uma ação é passada diretamente para a prop action de um formulário, o navegador envia o formulário nativamente por meio de uma requisição POST mesmo antes de o React hidratar. O Next.js intercepta e executa a ação no servidor, então o formulário funciona sem JavaScript no cliente e evolui para uma experiência conduzida pelo cliente após a hidratação.
Qual é a diferença entre lançar e retornar um erro em uma ação? Lançar propaga o erro até o error boundary mais próximo e é adequado para falhas inesperadas. Retornar um objeto de estado estruturado por meio de useActionState é adequado para erros de validação esperados, que devem ser renderizados inline sem desmontar o formulário.
Chamar revalidatePath refaz a busca dos dados imediatamente? Não. Ele marca as entradas em cache como obsoletas para que a próxima requisição as regenere. A resposta atual não é afetada, e é por isso que um redirecionamento após uma mutação chega a um conteúdo recém-renderizado.
Para o contexto de renderização mais amplo em que essas mutações rodam, a análise React 19 Server Components em produção combina naturalmente com o conhecimento sobre Server Actions.
Pronto para mandar bem nas entrevistas de React / Next.js?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Conclusão
As Server Actions do Next.js 16 consolidam mutações, revalidação e aprimoramento progressivo em uma única primitiva server-first. Os principais pontos:
- Marque mutações com
"use server"e passe-as para a propactionde um formulário para obter tratamento de rede e aprimoramento progressivo de graça. - Conduza os estados de pendência e validação com
useActionState, retornando um objeto estruturado para erros esperados em vez de lançar exceções. - Use
revalidatePathquando uma rota muda erevalidateTagquando os mesmos dados se espalham por várias rotas. - Use
useOptimisticpara renderizar o resultado esperado instantaneamente e deixe o React reconciliar ou reverter automaticamente. - Autorize e valide dentro do corpo de cada ação, porque cada ação utilizada é um endpoint HTTP público que o middleware sozinho não protege.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

Cache Components no Next.js 16 em 2026: use cache, PPR e Perguntas de Entrevista
Análise aprofundada dos Cache Components do Next.js 16: a diretiva use cache, Partial Pre-Rendering (PPR), cacheLife, cacheTag e perguntas de entrevista técnica para desenvolvedores seniores.

React Compiler em 2026: memoização automática e perguntas de entrevista
Análise completa do React Compiler em 2026: pipeline de compilação, memoização automática, regras do React e perguntas frequentes em entrevistas técnicas.

Testes em React 2026: Vitest, React Testing Library e Melhores Práticas
Dominar os testes em React com Vitest e React Testing Library. Aprender padrões de teste de componentes, tratamento assíncrono, estratégias de mocking e melhores práticas para entrevistas técnicas em 2026.