Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Usa useSWRMutation para mutaciones remotas (POST, PUT, DELETE) y mutate para actualizaciones locales de caché. Combina ambos para patrones de UI optimista que se actualizan al instante y se reconcilian con el servidor.
"use client";
import useSWRMutation from "swr/mutation";
async function createPost(url: string, { arg }: { arg: { title: string; body: string } }) {
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(arg),
});
return res.json();
}
function NewPostForm() {
const { trigger, isMutating } = useSWRMutation("/api/posts", createPost);
const handleSubmit = async (formData: FormData) => {
await trigger({
title: formData.get("title") as string,
body: formData.get("body") as string,
});
};
return (
<form action={handleSubmit}>
<input name="title" required />
<textarea name="body" required />
<button disabled={isMutating}>
{isMutating ? "Creando..." : "Crear publicación"}
</button>
</form>
);
}"use client";
import useSWR, { useSWRConfig } from "swr";
import useSWRMutation from "swr/mutation";
interface Todo {
id: number;
text: string;
done: boolean;
}
const fetcher = (url: string): Promise<Todo[]> => fetch(url).then((r) => r.json());
async function toggleTodo(url: string, { arg }: { arg: { id: number; done: boolean } }) {
return fetch(`${url}/${arg.id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ done: arg.done }),
}).then((r) => r.json());
}
export default function TodoList() {
const { data: todos, mutate } = useSWR<Todo[]>("/api/todos", fetcher);
const { trigger } = useSWRMutation("/api/todos", toggleTodo);
const handleToggle = async (todo: Todo) => {
const newDone = !todo.done;
// Actualización optimista
await mutate(
async (current) => {
await trigger({ id: todo.id, done: newDone });
return current?.map((t) => (t.id === todo.id ? { ...t, done: newDone } : t));
},
{
optimisticData: todos?.map((t) =>
t.id === todo.id ? { ...t, done: newDone } : t
),
rollbackOnError: true,
revalidate: false,
}
);
};
return (
<ul>
{todos?.map((todo) => (
<li key={todo.id} onClick={() => handleToggle(todo)}>
<span style={{ textDecoration: todo.done ? "line-through" : "none" }}>
{todo.text}
</span>
</li>
))}
</ul>
);
}useSWRMutation está diseñado para mutaciones que no deben ejecutarse automáticamente. Devuelve una función trigger que llamas manualmente.trigger recibe un arg que se pasa a tu función de mutación como { arg }.mutate de useSWR) está limitado a la clave del hook. Actualiza la caché local para esa clave específica.useSWRConfig) puede actualizar cualquier clave de caché desde cualquier parte de tu app.optimisticData actualiza la caché de inmediato antes de que la mutación asincrónica se resuelva.rollbackOnError: true revierte la actualización optimista si la mutación lanza un error.revalidate: false omite volver a obtener datos tras la mutación cuando confías en la actualización local.mutate global para invalidar claves relacionadas:
import { useSWRConfig } from "swr";
function AddComment({ postId }: { postId: string }) {
const { mutate } = useSWRConfig();
const handleAdd = async (text: string) => {
await fetch(`/api/posts/${postId}/comments`, {
method: "POST",
body: JSON.stringify({ text }),
});
// Revalidar múltiples claves
mutate(`/api/posts/${postId}`);
mutate(`/api/posts/${postId}/comments`);
};
}Mutación con datos devueltos:
const { trigger } = useSWRMutation("/api/posts", createPost, {
onSuccess(data) {
// data es el valor de retorno de createPost
console.log("Creado:", data.id);
},
});Poblar la caché tras la mutación:
const { trigger } = useSWRMutation("/api/posts", createPost, {
populateCache: (newPost, currentPosts) => [...(currentPosts ?? []), newPost],
revalidate: false,
});useSWRMutation<Data, Error, Key, Arg> acepta cuatro genéricos para seguridad de tipos completa.(key: Key, options: { arg: Arg }) => Promise<Data>.const { trigger } = useSWRMutation<Todo, Error, string, { text: string }>(
"/api/todos",
async (url, { arg }) => {
const res = await fetch(url, {
method: "POST",
body: JSON.stringify(arg),
});
return res.json();
}
);
// trigger({ text: "Buy milk" }) - totalmente tipadouseSWRMutation y useSWR usan la misma caché. Si ambos usan la misma clave, comparten datos. Esto suele ser lo que quieres, pero ten en cuenta que el trigger de useSWRMutation puede sobrescribir los datos en caché de useSWR.optimisticData debe ser el estado nuevo completo, no una actualización parcial. Si tienes una lista, debes devolver la lista actualizada completa.rollbackOnError: true, una mutación fallida deja datos optimistas obsoletos en la caché.isMutating permanece en true hasta que la promesa de trigger se resuelve. Si navegas fuera antes de que se resuelva, no hay limpieza a menos que el componente se desmonte.mutate() sin argumentos revalida la clave volviendo a obtener datos. Llamar a mutate(newData) establece los datos sin revalidación.| Enfoque | Ventajas | Desventajas |
|---|---|---|
| useSWRMutation | Diseñado para ello, se integra con la caché | Requiere una importación separada |
| mutate vinculado + fetch | Más simple para actualizaciones básicas de caché | Manejo manual de errores |
| React Query useMutation | Más reintentos y hooks de ciclo de vida integrados | Biblioteca distinta |
| Server Actions (Next.js) | Sin JS en el cliente, nativo de formularios | Sin UI optimista sin trabajo adicional |
useSWRMutation es para mutaciones remotas (POST, PUT, DELETE) activadas manualmente mediante trigger.mutate de useSWR (vinculado) actualiza la caché local para una clave específica.mutate global de useSWRConfig puede actualizar cualquier clave de caché desde cualquier lugar.El argumento pasado a trigger(arg) está disponible en la función de mutación como { arg }:
async function createPost(url: string, { arg }: { arg: { title: string } }) {
return fetch(url, {
method: "POST",
body: JSON.stringify(arg),
}).then((r) => r.json());
}
const { trigger } = useSWRMutation("/api/posts", createPost);
await trigger({ title: "Hello" });optimisticData actualiza la caché de inmediato antes de que la mutación asincrónica se resuelva. Debe ser el estado nuevo completo (p. ej., la lista actualizada entera), no una actualización parcial, porque SWR reemplaza toda la entrada de caché con ese valor.
Si la mutación falla, los datos optimistas obsoletos permanecen en la caché de forma permanente. La UI mostrará datos que no coinciden con el estado del servidor. Establece siempre rollbackOnError: true cuando uses optimisticData.
Usa mutate global de useSWRConfig:
const { mutate } = useSWRConfig();
mutate(`/api/posts/${postId}`);
mutate(`/api/posts/${postId}/comments`);Te permite actualizar la caché directamente con el valor de retorno de la mutación sin una revalidación fetch:
const { trigger } = useSWRMutation("/api/posts", createPost, {
populateCache: (newPost, currentPosts) => [...(currentPosts ?? []), newPost],
revalidate: false,
});Sí. Si ambos usan la misma clave, comparten los datos en caché. El trigger de useSWRMutation puede sobrescribir los datos que useSWR está mostrando. Esto suele ser intencional, pero puede sorprender si no lo esperas.
isMutating permanece en true hasta que la promesa de trigger se resuelve. Si el componente se desmonta antes de la resolución, no hay limpieza. Ten cuidado al navegar durante mutaciones en curso.
const { trigger } = useSWRMutation<Todo, Error, string, { text: string }>(
"/api/todos",
async (url, { arg }) => {
const res = await fetch(url, {
method: "POST",
body: JSON.stringify(arg),
});
return res.json();
}
);
// trigger({ text: "Buy milk" }) está totalmente tipadoLa firma es (key: Key, options: { arg: Arg }) => Promise<Data>. Los cuatro genéricos en useSWRMutation<Data, Error, Key, Arg> controlan los tipos del dato de retorno, el error, la clave y el argumento pasado a trigger.
mutate() sin argumentos revalida volviendo a obtener datos del servidor.mutate(newData) establece los datos de la caché directamente sin volver a obtener.Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥