Contexto
SWR leva o nome do próprio padrão HTTP que implementa — stale-while-revalidate: mostra o dado em cache imediatamente e dispara uma revalidação em paralelo, atualizando a UI quando a resposta chega. TanStack Query (o antigo React Query) nasceu do mesmo problema — buscar dados assíncronos em componentes React sem reescrever, a cada tela, a mesma lógica de loading, erro, cache e revalidação em segundo plano — mas foi construído como uma camada de cache mais completa, com seu próprio client, ciclo de vida de queries e um conjunto de primitivas para mutações que SWR deixa fora do núcleo.
A diferença mais visível não é técnica, é de escopo: SWR foi desenhado pela Vercel para ficar pequeno e fazer bem uma coisa — buscar e revalidar. TanStack Query foi desenhado para crescer junto com a aplicação, cobrindo cache normalizado, infinite scroll, mutações otimistas e devtools, e hoje tem adaptadores para além do React — Vue, Svelte, Solid e Angular compartilham o mesmo core. Essa diferença de ambição já aparece na instalação: TanStack Query pede um QueryClientProvider envolvendo a árvore de componentes antes de qualquer useQuery funcionar, enquanto SWR roda sem nenhum provider obrigatório, com cache global implícito por chave.
Quando escolher TanStack Query
TanStack Query compensa quando a aplicação faz mais do que ler dados: ela também escreve, e essas escritas precisam invalidar ou atualizar o cache de forma previsível. O hook useMutation é cidadão de primeira classe, com callbacks (onSuccess, onError, onSettled) e integração direta com o queryClient para invalidar as queries afetadas:
import { useMutation, useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: updateTodo,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["todos"] }),
});
Esse queryClient centralizado é o que sustenta os recursos mais avançados: useInfiniteQuery para paginação e scroll infinito sem reimplementar acúmulo de páginas manualmente, prefetch antecipado de rotas, hidratação de cache vindo do servidor, e devtools oficiais que mostram o estado de cada query — fresh, stale, fetching, inactive — em tempo real durante o desenvolvimento. O custo é conceitual: para tirar proveito da ferramenta, o time precisa entender query keys, tempo de stale, tempo de cache e a diferença entre invalidar e refazer uma query, conceitos que não existem no vocabulário do SWR.
Quando escolher SWR
SWR encaixa quando o que a aplicação precisa é, na prática, buscar dados e mantê-los frescos — sem construir em cima disso uma camada de mutações, paginação ou hidratação sofisticada. Um único hook cobre a maior parte dos casos:
import useSWR from "swr";
const { data, error, isLoading } = useSWR("/api/todos", fetcher);
Por padrão, SWR já revalida ao focar a janela, ao reconectar a rede e em intervalos configuráveis, o que cobre boa parte do que times normalmente reimplementam manualmente com fetch cru. Para mutações, a biblioteca oferece duas saídas: a função global mutate(), que atualiza o cache de uma chave específica de fora do componente, e o addon useSWRMutation, que empacota uma mutação como hook próprio quando o padrão global não é suficiente. Nenhuma das duas tenta ser tão completa quanto o useMutation do TanStack Query — e é exatamente essa economia de conceitos que faz SWR permanecer simples de auditar mesmo em uma base de código grande, já que qualquer desenvolvedor consegue entender o fluxo de dados sem aprender um vocabulário próprio de cache.
Veredito
As duas bibliotecas resolvem a mesma dor de fetching com qualidade e mantenedores ativos por trás — Vercel no caso do SWR, a equipe TanStack no outro. O que muda de verdade é quanta infraestrutura de cache a aplicação já precisa carregar hoje, e não daqui a um ano: se o produto tem telas de escrita intensa, listas paginadas e times acostumados a devtools de cache, a superfície maior do TanStack Query paga por si mesma cedo. Se o que existe são poucas chamadas de leitura que só precisam ficar atualizadas sem drama, adicionar um client de cache dedicado para isso é resolver um problema que a aplicação ainda não tem — e é aí que a economia de conceitos do SWR vale mais do que qualquer feature extra.