Voltar para Artigos
Boas Práticas

A Ilusão da Latência Zero: Optimistic Updates no Next.js

Fábio Flauzino
Fábio Flauzino
Senior Front-End Engineer
27 de jul. de 2026
5 min de leitura
A Ilusão da Latência Zero: Optimistic Updates no Next.js

Arquitetura Moderna para CRUDs no Next.js

  1. Parte 1: Arquitetando um CRUD Escalável
  2. Parte 2: Construindo uma Camada Profissional de Comunicação
  3. Parte 3: Gerenciando Estado Remoto com TanStack Query
  4. Parte 4: Formulários Escaláveis com React Hook Form + Zod
  5. Parte 5: Search Params, Filtros e Paginação
  6. 👉 Parte 6: UX Moderna: Optimistic Updates e Performance (Você está aqui)

Até aqui resolvemos a engenharia.

Construímos uma infraestrutura sólida, protegemos as mutações com o Zod, sincronizamos a navegação com Search Params e delegamos o gerenciamento do estado remoto ao TanStack Query.

Nossa aplicação é segura, manutenível e escalável.

Mas, para o usuário final, nada disso importa se o sistema parecer lento.

Neste último capítulo da série, vamos deixar de olhar apenas para a arquitetura do código e começar a olhar para a percepção de performance.


O Problema da Latência Inevitável

Imagine que o nosso CRUD de usuários já está em produção.

Um administrador encontra um usuário spammer na tabela e clica no botão "Deletar".

O que acontece em uma implementação tradicional?

  1. O usuário clica no botão.
  2. O botão exibe um spinner de carregamento.
  3. A requisição viaja pela rede, chega ao servidor, exclui o registro no banco de dados e retorna um 200 OK.
  4. O TanStack Query invalida a queryKey e busca novamente a lista de usuários.
  5. O spinner desaparece e o usuário some da tabela.

Mesmo com uma infraestrutura excelente, sempre existirá a latência da rede.

Ela faz parte da comunicação entre cliente e servidor e simplesmente não pode ser eliminada.

Em uma conexão rápida, esse processo pode levar algumas centenas de milissegundos.

Em uma conexão instável, como uma rede móvel, pode levar vários segundos.

Para o cérebro humano, qualquer interação que demore mais de aproximadamente 100 ms para responder já reduz a sensação de instantaneidade.

É exatamente esse atraso que faz uma aplicação parecer lenta.


A Ilusão da Latência Zero (Optimistic Updates)

O que é um Optimistic Update (Atualização Otimista)?

É a técnica de assumir que a operação enviada ao servidor será concluída com sucesso.

Em vez de esperar a confirmação da API, antecipamos a atualização da interface imediatamente após a ação do usuário.

Enquanto isso, a requisição continua sendo executada normalmente em segundo plano.

Se tudo ocorrer como esperado, nenhuma alteração adicional é necessária.

Caso ocorra algum erro, desfazemos a alteração temporária e sincronizamos novamente a interface.

O resultado é uma experiência extremamente fluida.

Para o usuário, a aplicação transmite a sensação de que está executando todas as operações localmente.

É justamente esse tipo de detalhe que diferencia aplicações comuns de interfaces modernas.


O Servidor Continua Sendo a Fonte da Verdade

Existe um detalhe conceitual extremamente importante.

Um Optimistic Update não altera a responsabilidade do servidor.

O banco de dados continua sendo a fonte definitiva da verdade.

O que modificamos é apenas a representação temporária desses dados dentro do cache do TanStack Query.

Se a operação falhar, descartamos essa representação otimista e sincronizamos novamente a interface com a resposta real do servidor.

O fluxo arquitetural acontece exatamente assim:

Usuário clica em "Excluir"
            ↓
        onMutate
            ↓
Cache do TanStack Query
(atualização otimista)
            ↓
Interface atualizada
            ↓
Server Action
            ↓
Service
            ↓
API
            ↓
───────────────────────────
Sucesso → invalidateQueries()

Erro → rollback do cache

Manipulando o Cache (A Prática)

Para implementar esse fluxo utilizamos três ciclos de vida fundamentais do useMutation.

onMutate

É executado no exato instante em que a mutação começa, antes mesmo da requisição sair do navegador.

É aqui que:

  • cancelamos consultas em andamento;
  • fazemos um backup do cache atual;
  • aplicamos a atualização otimista.

onError

É executado somente quando a operação falha.

Nesse momento utilizamos o backup salvo anteriormente para restaurar o estado original da aplicação.

Esse processo é conhecido como rollback.

onSettled

É executado sempre ao final da mutação, independentemente de sucesso ou erro.

Seu objetivo é invalidar o cache para garantir que a interface reflita exatamente o estado armazenado no servidor.

A implementação fica assim:

"use client";

import { useMutation, useQueryClient } from "@tanstack/react-query";

import { deleteUserAction } from "@/actions/users/delete-user-action";
import { User } from "@/types/user";

export function useDeleteUser() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: deleteUserAction,

    onMutate: async (userIdToDelete) => {
      // 1. Cancela buscas em andamento
      await queryClient.cancelQueries({
        queryKey: ["users"],
      });

      // 2. Faz um backup do cache atual
      const previousUsers =
        queryClient.getQueryData(["users"]);

      // 3. Atualiza o cache imediatamente
      queryClient.setQueryData(
        ["users"],
        (oldData: any) => {
          if (!oldData) return oldData;

          return {
            ...oldData,
            items: oldData.items.filter(
              (user: User) =>
                user.id !== userIdToDelete
            ),
          };
        }
      );

      return {
        previousUsers,
      };
    },

    onError: (_, __, context) => {
      queryClient.setQueryData(
        ["users"],
        context?.previousUsers
      );

      alert(
        "Falha ao deletar usuário. Tente novamente."
      );
    },

    onSettled: () => {
      queryClient.invalidateQueries({
        queryKey: ["users"],
      });
    },
  });
}

Observe que não estamos modificando a tabela diretamente.

Também não criamos estados temporários utilizando useState.

Toda a alteração acontece dentro do cache do TanStack Query.

A interface simplesmente reage ao novo estado remoto.


Quando NÃO Utilizar Optimistic Updates

Atualizações otimistas são extremamente úteis, mas devem ser aplicadas com critério.

Situações recomendadas

  • Exclusão de registros.
  • Favoritar ou curtir conteúdos.
  • Alteração de status.
  • Movimentação de cartões em um Kanban.
  • Alterações simples em registros existentes.

Situações em que devem ser evitadas

  • Operações financeiras.
  • Confirmação de pagamentos.
  • Transferências bancárias.
  • Criação de registros que dependem de um identificador definitivo gerado pelo servidor.

Nesses casos, criar um estado otimista pode gerar inconsistências difíceis de explicar para o usuário.

Uma regra prática costuma funcionar muito bem:

Se uma falha puder causar confusão significativa ou comprometer a confiança do usuário, prefira aguardar a confirmação do servidor.


O Ponto de Chegada

Ao longo desta série adicionamos uma camada de arquitetura por vez.

Primeiro organizamos a comunicação através de Services e Server Actions.

Depois centralizamos o estado remoto utilizando TanStack Query.

Em seguida estruturamos os formulários e os contratos de dados com React Hook Form e Zod.

Transformamos a URL em parte da arquitetura utilizando Search Params.

Por fim, utilizamos toda essa base para entregar uma experiência de usuário extremamente fluida através dos Optimistic Updates.

Nenhuma dessas camadas substituiu a anterior.

Cada uma resolveu exatamente um problema específico, comunicando-se de forma previsível com as demais.

Essa é justamente a essência de uma arquitetura escalável.

Ao final destes seis capítulos, não construímos apenas um CRUD.

Construímos uma arquitetura onde cada camada possui uma única responsabilidade, permitindo que a aplicação evolua de forma previsível, testável e sustentável.

Construir software não é empilhar bibliotecas populares.

É entender onde cada responsabilidade começa e termina, para que a aplicação possa crescer, mudar e evoluir sem que a base precise ser reconstruída.

Gostou deste artigo? Compartilhe com sua rede.