Guía completa de SWR: domina estrategias de caché y actualizaciones optimistas en la práctica

Miras la pantalla, el spinner de carga de siempre — por enésima vez recargas la lista de usuarios. Cada cambio de pestaña: carga. Cada vuelta desde otra página: carga. Incluso si atiendes una llamada y el navegador pierde el foco, al regresar vuelve la carga. ¿Lo peor? Esos datos se cargaron hace un segundo.
En un proyecto React, cada obtención de datos implica useState, useEffect, más la gestión de loading y errores. Veinte líneas de código como mínimo. Peor aún: varios componentes necesitan los mismos datos — o subes el estado al padre (más código), o cada componente hace su propia petición (desperdicio de ancho de banda).
Cuando descubrí SWR, la sensación fue parecida a la primera vez que usé Git en lugar de copiar carpetas a mano. Tres líneas de código, el 80 % de los problemas resueltos. Sin exagerar.
Hoy hablamos de SWR — la biblioteca de obtención de datos para React de Vercel. El principio central se resume en una palabra: stale-while-revalidate (obsoleto – revalidar). ¿Suena técnico? La idea es simple: primero ves la «foto antigua» en caché (rápido), en segundo plano se toma una nueva (actualizada), y cuando está lista se reemplaza sin que lo notes (sin saltos).
¿Por qué SWR? Tres puntos débiles del fetch clásico
Veamos cómo es la obtención de datos clásica en React. Supongamos que quieres mostrar una lista de usuarios:
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
Veinticuatro líneas de código — solo para mostrar una lista. ¿Te parece aceptable? Piensa en estos escenarios:
Punto débil 1: boilerplate repetitivo
Cada componente que necesita datos sigue el mismo patrón. Lista de usuarios, de artículos, de comentarios — tres estados, montones de if, gestión de errores… El copiar y pegar se vuelve vergonzoso. Una vez conté: en un proyecto mediano, este tipo de fragmentos representaba alrededor del 20 % del código.
Punto débil 2: ¿caché? No existe
Lo más frustrante: sin caché. El usuario va de la portada a un detalle y vuelve — otra larga carga. Los datos tienen treinta segundos y aun así parte una nueva petición. Los usuarios dicen «vuestra web es lenta» — a menudo no es la API, es la ausencia total de caché.
Puedes montar gestión de estado global — Redux, Zustand, store, actions, reducers… Una función de tres minutos se convierte en treinta de arquitectura.
Punto débil 3: sincronizar datos entre componentes
Varios componentes, mismos datos: mensajes no leídos en la cabecera, la barra lateral y la página de mensajes. ¿Subir el estado hasta arriba? Props en diez niveles. ¿Context? Cada actualización re-renderiza todo el árbol.
Una vez pasé dos días en una función de notificaciones sencilla — al hacer commit, el diff solo mostraba setState y useEffect. Me daba vergüenza a mí mismo.
Por eso SWR es popular. No es otra herramienta más — resuelve problemas reales. El mismo código con SWR:
import useSWR from 'swr';
function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;
return <ul>{data.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
Nueve líneas. Listo. Con caché, revalidación automática y compartición entre componentes. El fetcher es tu función fetch — en general se define una vez de forma global:
const fetcher = url => fetch(url).then(r => r.json());
La diferencia habla por sí sola.
Concepto central: la estrategia Stale-While-Revalidate
El nombre SWR viene de la estrategia de caché HTTP definida en la RFC 5861. No te asustes por «RFC» — el principio es simple.
Analogía con una foto: quieres una foto reciente de un amigo. Método clásico — llamarlo, esperar a que tome una y te la envíe. Con SWR — primero sacas la foto antigua de vuestra última quedada (quizá de la semana pasada), la miras mientras le envías un mensaje para que tome una nueva, y la reemplazas cuando llega.
La diferencia: no esperas con las manos vacías. Contenido inmediato (quizá un poco desactualizado), garantizando frescura al final. Eso es stale-while-revalidate — mostrar lo obsoleto, revalidar en paralelo.
Flujo completo de SWR:
-
Paso 1: devolver la caché de inmediato (stale)
- Al montar, SWR comprueba la caché local
- ¿Hay caché? Retorno inmediato, página al instante
- ¿Sin caché?
undefined, se muestra loading
-
Paso 2: petición en segundo plano (revalidate)
- Con o sin caché: petición API
- El usuario no lo nota, el contenido ya está ahí
-
Paso 3: actualizar los datos
- Tras la respuesta: caché y UI actualizadas discretamente
- Si los datos cambian: React re-renderiza
- Si no cambian: no pasa nada
Ejemplo concreto — mostrar un precio de acción:
function StockPrice({ symbol }) {
const { data, error } = useSWR(`/api/stock/${symbol}`, fetcher);
return (
<div>
<h2>{symbol}</h2>
<p>Precio: {data ? `$${data.price}` : 'Loading...'}</p>
<span>Actualizado: {data?.updatedAt}</span>
</div>
);
}
En la primera apertura, data es undefined — «Loading…». Un segundo después, aparece el precio.
Cambio de pestaña, diez minutos de correo, vuelta — precio al instante desde caché, sin flash de loading. En paralelo, SWR lanza una nueva petición. ¿Precio cambiado? → UI actualizada; sin cambios → se mantiene.
Tres momentos de revalidación automática por defecto:
- Componente remontado (
revalidateOnMount): recarga o vuelta de ruta - Ventana de nuevo al foco (
revalidateOnFocus): vuelta a la pestaña del navegador - Red restablecida (
revalidateOnReconnect): tras una desconexión
No tienes que configurar nada — SWR mantiene los datos frescos. Media hora de correo, vuelta a tu sitio → refresh automático. Metro sin red, vuelta con 4G → recarga automática.
Cuando descubrí que era el comportamiento por defecto, me sorprendí. Antes: o datos caducados, o escuchar manualmente visibilitychange, online — mucho código. Ahora: gratis.
El concepto de key:
El primer parámetro de useSWR es una cadena como '/api/users' — la key, central.
// Dos componentes, misma key
function Header() {
const { data } = useSWR('/api/user', fetcher);
return <div>Bienvenido, {data?.name}</div>;
}
function Profile() {
const { data } = useSWR('/api/user', fetcher);
return <div>Perfil: {data?.email}</div>;
}
Misma key → datos compartidos. Una sola petición API, ambos componentes sincronizados.
Sin gestión de estado, sin Context — solo la key.
Estrategias de caché en detalle: hacer el fetch más inteligente
SWR cachea y revalida automáticamente — en la práctica, distintos datos exigen distinta «frescura». El avatar cambia poco, el precio de una acción cada segundo. Hay que ajustar la estrategia.
El comportamiento por defecto es listo, pero no universal
SWR es bastante agresivo por defecto:
- Revalidación en cada montaje
- Revalidación al enfocar
- Revalidación al reconectar
Perfecto para tiempo real (chat, estado en línea). Para datos estables (lista de artículos, perfil), a veces es excesivo — pestaña ida y vuelta, petición cada vez. Innecesario.
Opciones de configuración esenciales
const { data } = useSWR('/api/articles', fetcher, {
revalidateOnFocus: false, // sin petición al enfocar
revalidateOnReconnect: false, // sin petición al reconectar
refreshInterval: 0, // intervalo de polling en ms, 0 = desactivado
dedupingInterval: 2000, // deduplicar peticiones idénticas en 2 s
});
Los nombres son explícitos.
Escenario 1: datos en tiempo real (acciones, contador en línea)
const { data } = useSWR('/api/stock/AAPL', fetcher, {
refreshInterval: 1000, // polling cada segundo
revalidateOnFocus: true // actualización inmediata al volver
});
Máxima frescura — el polling es simple (WebSocket sería mejor, otro tema).
Escenario 2: datos relativamente estables (perfil, lista de artículos)
const { data } = useSWR('/api/profile', fetcher, {
revalidateOnFocus: false,
refreshInterval: 0,
dedupingInterval: 60000, // sin duplicados durante 1 minuto
});
El perfil cambia poco — cambio de pestaña sin refresh. Tras una edición manual, puedes disparar actualización con mutate (más adelante).
Escenario 3: contenido casi estático (documentación, ayuda)
const { data } = useSWR('/api/docs', fetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
revalidateOnMount: false,
revalidateIfStale: false,
});
Datos que pueden quedarse un mes sin cambiar — tras la primera carga, caché casi permanente hasta recarga manual.
Configuración global vs local
Estrategia uniforme en toda la app con SWRConfig:
import { SWRConfig } from 'swr';
function App() {
return (
<SWRConfig value={{
refreshInterval: 3000,
fetcher: (url) => fetch(url).then(r => r.json()),
revalidateOnFocus: false,
}}>
<Dashboard />
</SWRConfig>
);
}
Todos los useSWR hijos heredan estos valores; un Hook puede sobrescribir:
// revalidateOnFocus: true sobrescribe el false global
const { data } = useSWR('/api/realtime', fetcher, {
revalidateOnFocus: true
});
Deduplicación de peticiones: ahorrar tu cuota API
Tres componentes montan a la vez con useSWR('/api/user') — SWR solo envía una petición, todos comparten el resultado.
Ventana por defecto: 2 segundos (dedupingInterval: 2000). Si tus componentes se remontan rápido (cambio de pestaña veloz), sube este valor.
Esta función me salvó una cuota API: un scroll rápido disparaba la misma petición en ráfaga — con deduplicación, calma inmediata. Síntoma corregido, pero corrige también la causa en el código.
Fetch condicional: peticiones dependientes
Primero A, luego B — una key en null pausa:
const { data: user } = useSWR('/api/user', fetcher);
const { data: projects } = useSWR(
user ? `/api/projects?userId=${user.id}` : null,
fetcher
);
Sin user, no hay petición; en cuanto user está, arranca projects. Dependencia secuencial, esfuerzo mínimo.
Actualizaciones optimistas: el arma secreta para mejor UX
La caché resuelve la velocidad — para acciones del usuario queda una pregunta: ¿mostrar el corazón de «Me gusta» tras la respuesta API, o al instante?
Actualización optimista (Optimistic Update): asumir éxito, actualizar la UI de inmediato, revertir si falla. Parece arriesgado — la mayoría de acciones tienen éxito, la mejora de UX es visible.
Clásico vs optimista
Clásico:
- Clic en «Me gusta»
- Botón en loading o deshabilitado
- Espera 500 ms–2 s
- Corazón encendido
- Usuario: «Este sitio es lento…»
Optimista:
- Clic
- Corazón al instante
- Petición en segundo plano
- A menudo éxito — nada más
- A veces fallo — corazón gris, mensaje de error
Latencia percibida: 0 ms.
mutate: controlar la caché manualmente
import { mutate } from 'swr';
// Disparar la revalidación de /api/user
mutate('/api/user');
Para optimismo hace falta más — ejemplo Todo completo:
import useSWR, { mutate } from 'swr';
function TodoList() {
const { data: todos } = useSWR('/api/todos', fetcher);
const addTodo = async (text) => {
const newTodo = { id: Date.now(), text, completed: false };
mutate(
'/api/todos',
async (currentTodos) => {
const optimisticData = [...currentTodos, newTodo];
const savedTodo = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo)
}).then(r => r.json());
return [...currentTodos, savedTodo];
},
{
optimisticData: [...todos, newTodo],
rollbackOnError: true,
revalidate: false,
}
);
};
return (
<div>
{todos?.map(todo => <div key={todo.id}>{todo.text}</div>)}
<button onClick={() => addTodo('New task')}>Add</button>
</div>
);
}
Flujo: clic → nuevo Todo inmediato (optimisticData) → POST → éxito: datos del servidor (id, timestamps) → fallo: rollback (rollbackOnError: true).
Cuatro opciones centrales
1. optimisticData — visualización inmediata
optimisticData: [...todos, newTodo]
O como función:
optimisticData: (currentTodos) => [...currentTodos, newTodo]
2. populateCache — ¿actualizar la caché con el valor de retorno?
Por defecto true. El servidor suele añadir id, createdAt — entonces útil. Respuesta API incompleta → false.
3. revalidate — ¿refetch?
En general false — ya actualizaste manualmente. Solo si quieres que SWR confirme.
4. rollbackOnError — ¿volver atrás ante error?
Importante: error en mutate → retorno automático al estado anterior. Corazón encendido, API falla → corazón apagado.
Control más fino:
rollbackOnError: (error) => {
return error.name !== 'AbortError';
}
Práctica: eliminar un Todo
const deleteTodo = async (id) => {
mutate(
'/api/todos',
async (currentTodos) => {
const optimistic = currentTodos.filter(t => t.id !== id);
await fetch(`/api/todos/${id}`, { method: 'DELETE' });
return optimistic;
},
{
optimisticData: todos.filter(t => t.id !== id),
rollbackOnError: true,
}
);
};
Clic → Todo desaparece. Error del servidor → Todo vuelve, mensaje de error.
useSWRMutation — sintaxis más elegante
Desde SWR 2.0:
import useSWRMutation from 'swr/mutation';
async function updateUser(url, { arg }) {
await fetch(url, {
method: 'POST',
body: JSON.stringify(arg)
});
}
function Profile() {
const { trigger, isMutating } = useSWRMutation('/api/user', updateUser);
return (
<button
onClick={() => trigger({ name: 'John' })}
disabled={isMutating}
>
Update Name
</button>
);
}
trigger lanza la mutación, isMutating sustituye el loading manual.
¿Cuándo usar optimismo?
✅ Adecuado:
- Like/favorito (pocos fallos, reversible)
- Añadir/eliminar en una lista
- Toggle (notificaciones)
- Guardado de borrador
❌ Inadecuado:
- Pago (esperar confirmación)
- Borrado de cuenta (irreversible)
- Contraseña, permisos
- Resultados calculados en servidor (informe)
Regla: baja tasa de fallo, reversible, UX prioritaria → optimista. Crítico o irreversible → esperar al servidor.
Un error vivido: comentarios con optimismo — red lenta, el usuario pulsa «Enviar» varias veces, decenas de entradas idénticas en la UI. Solución: isMutating, botón deshabilitado durante la petición. Optimismo sí — pero prevé casos límite.
SWR vs React Query: cómo elegir
Ambos son excelentes y se mantienen activamente en 2025. Una mala elección rara vez es fatal — la buena simplifica el desarrollo.
Tamaño: SWR más ligero
- SWR: 5,3 KB (gzip)
- React Query (TanStack Query): 16,2 KB
Con presupuesto de bundle estricto (landing, H5 móvil), SWR cuenta. En muchas apps, ~10 KB son despreciables.
Complejidad: SWR más simple
API central: un useSWR. Se entiende en minutos.
React Query: QueryClient, useQuery, useMutation, queryKeys, cache time vs stale time — curva de aprendizaje más pronunciada, documentación larga.
Equipos con menos experiencia → a menudo SWR.
Funcionalidades: React Query más completo
- DevTools oficiales — queries, caché, refetch visibles. SWR sin DevTools oficiales.
- Control de caché más fino — cache time y stale time separados; SWR: caché + revalidate.
- Paginación/infinite —
useInfiniteQueryvsuseSWRInfinite, React Query algo más maduro. - Mutaciones —
useMutationcon estado global, retry, callbacks;useSWRMutationdesde 2.0, más ligero.
Comunidad
TanStack Query: comunidad más grande, más respuestas en Stack Overflow.
SWR: Vercel, integración estrecha con Next.js — con Next.js, elección natural.
Recomendación
| SWR | React Query |
|---|---|
| Lógica de datos simple | Necesidades de caché complejas |
| Bundle sensible | +10 KB aceptable |
| Next.js | CRA, Vite, otros |
| Poca experiencia frontend | Equipo que prefiere herramientas ricas |
| Arranque rápido | Tiempo para el ecosistema |
Mi experiencia: MVP → SWR. Complejidad creciente → quizá React Query. La mayoría de veces, SWR basta.
La migración sigue siendo razonable — caché, revalidate, mutation son parecidos. No sobreanalices.
Next.js y SWR: una combinación natural
Ambos de Vercel — la integración es fluida.
App Router
Los Server Components son la norma; SWR es del lado cliente:
'use client';
import useSWR from 'swr';
export default function Profile() {
const { data } = useSWR('/api/user', fetcher);
return <div>{data?.name}</div>;
}
No olvides 'use client'.
SSR + fallback
Datos iniciales desde getStaticProps / getServerSideProps como fallback:
// pages/profile.js
export async function getStaticProps() {
const user = await fetch('https://api.example.com/user').then(r => r.json());
return {
props: {
fallback: {
'/api/user': user
}
},
revalidate: 60
}
}
export default function Profile({ fallback }) {
return (
<SWRConfig value={{ fallback }}>
<UserProfile />
</SWRConfig>
);
}
function UserProfile() {
const { data } = useSWR('/api/user', fetcher);
return <div>{data.name}</div>;
}
Primera visita: contenido completo (SEO), luego revalidación SWR en cliente.
Prefetch
import { mutate } from 'swr';
function ArticleLink({ id }) {
const prefetch = () => {
mutate(`/api/article/${id}`, fetch(`/api/article/${id}`).then(r => r.json()));
};
return (
<Link href={`/article/${id}`} onMouseEnter={prefetch}>
Read more
</Link>
);
}
El hover ya carga — el clic suele salir de caché.
Scroll infinito: useSWRInfinite
import useSWRInfinite from 'swr/infinite';
function ArticleList() {
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null;
return `/api/articles?page=${pageIndex + 1}&limit=10`;
};
const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher);
const articles = data ? data.flat() : [];
const isLoadingMore = isLoading || (size > 0 && data && typeof data[size - 1] === 'undefined');
return (
<div>
{articles.map(article => (
<div key={article.id}>{article.title}</div>
))}
<button
onClick={() => setSize(size + 1)}
disabled={isLoadingMore}
>
{isLoadingMore ? 'Loading...' : 'Load More'}
</button>
</div>
);
}
setSize(size + 1) carga la página siguiente; cada página queda en caché.
Rendimiento con Next.js
-
Frecuencia de revalidación
- Contenido estático: desactivar revalidación automática
- Datos de usuario: mantener revalidación al enfocar
- Tiempo real:
refreshInterval
-
Caché de rutas API
Cache-Controlen rutas API- SWR respeta cabeceras HTTP de caché
-
Evitar cascadas de peticiones
- Datos dependientes: fusionar en servidor si es posible
- O fetch paralelo en Next.js
Caso real: blog con SWR + ISR — HTML estático al instante, en segundo plano comprobación de artículos nuevos. Rápido y SEO-friendly.
Rendimiento estático más frescura dinámica — eso es Next.js + SWR.
Conclusión
SWR aborda tres problemas: boilerplate, ausencia de caché, sincronización entre componentes. stale-while-revalidate es rápido y actualizado. Revalidación, deduplicación y optimismo cubren la mayoría de casos.
Buenas prácticas:
- Adaptar la caché al tipo de datos — tiempo real con polling, estable con caché larga
- Optimismo para acciones reversibles — baja tasa de fallo requerida
- Compartir mediante keys — no Redux solo para datos API compartidos
- Next.js: fallback para SSR
- Simple → SWR, complejo → React Query — sin sobreingeniería
SWR no es una varita mágica — no corrige una API mala ni una arquitectura deficiente. Con una API sólida, el código frontend se vuelve mucho más elegante.
Prueba SWR en tu próximo proyecto — verás la diferencia.
Flujo completo de SWR
De la instalación a la configuración, el uso y la optimización de la caché
⏱️ Estimated time: 1 hr
- 1
Step 1: Instalación y uso básico
Instalación:
```bash
npm install swr
```
Uso básico:
```tsx
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then(r => r.json())
export function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher)
if (error) return <div>Failed to load</div>
if (isLoading) return <div>Loading...</div>
return <div>{data.map(user => <div key={user.id}>{user.name}</div>)}</div>
}
```
Puntos clave:
• Marcar el componente con 'use client'
• fetcher para la obtención de datos
• useSWR devuelve data, error, isLoading - 2
Step 2: Configurar opciones globales
Con SWRConfig:
```tsx
'use client'
import { SWRConfig } from 'swr'
const fetcher = (url: string) => fetch(url).then(r => r.json())
export function Providers({ children }) {
return (
<SWRConfig
value={{
fetcher,
revalidateOnFocus: true,
revalidateOnReconnect: true,
refreshInterval: 0,
}}
>
{children}
</SWRConfig>
)
}
```
Opciones habituales:
• revalidateOnFocus: revalidación al enfocar la ventana
• revalidateOnReconnect: revalidación al reconectar la red
• refreshInterval: actualización periódica (0 = desactivado)
• dedupingInterval: intervalo de deduplicación (2000 ms por defecto)
Punto clave: configurar en la raíz — todos los componentes hijos heredan - 3
Step 3: Integración Next.js (SSR)
Con fallback data:
```tsx
// app/users/page.tsx (Server Component)
import { getUsers } from '@/lib/users'
export default async function UsersPage() {
const initialUsers = await getUsers()
return <UsersList initialUsers={initialUsers} />
}
// components/UsersList.tsx (Client Component)
'use client'
import useSWR from 'swr'
export function UsersList({ initialUsers }) {
const { data } = useSWR('/api/users', fetcher, {
fallbackData: initialUsers
})
return <div>{data.map(...)}</div>
}
```
Ventajas:
• Primer pantallazo desde SSR — carga rápida
• Actualizaciones vía SWR — buena UX
• SSR y caché cliente combinados
Punto clave: usar fallbackData y no initialData (fallbackData no dispara revalidación) - 4
Step 4: Actualizaciones optimistas
Con mutate:
```tsx
import { mutate } from 'swr'
async function updateUser(id: string, name: string) {
mutate(`/api/users/${id}`, { ...user, name }, false)
await fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify({ name })
})
mutate(`/api/users/${id}`)
}
```
Puntos clave:
• El tercer parámetro false = sin revalidación
• Tras el éxito, mutate de nuevo para confirmar
• El usuario ve la actualización al instante
FAQ
¿Qué es SWR y por qué lo necesitas?
Puntos débiles del fetch clásico:
• useState, useEffect, loading y error por petición
• Veinte líneas como mínimo
• Datos compartidos: subir estado o peticiones duplicadas
Ventajas de SWR:
• Caché automático mediante las mismas keys
• Revalidación al enfocar y al reconectar
• Deduplicación de peticiones
• Reintento automático ante errores
• Un Hook simplifica hasta el 90 % del código
Tres líneas para el 80 % de casos:
```tsx
const { data, error, isLoading } = useSWR('/api/users', fetcher)
```
¿Cuál es la diferencia entre SWR y React Query?
• Ligero, baja curva de aprendizaje
• Encaja en la mayoría de proyectos
• Menos funcionalidades
• Vercel, buena integración Next.js
React Query:
• Más potente, más funcionalidades
• Escenarios complejos
• Curva de aprendizaje más alta
• Más flexible, más configuración
Recomendación:
• Proyecto simple → SWR
• Complejo → React Query
• Inseguro → SWR primero, migrar si hace falta
Punto clave: sin sobreingeniería — simple con SWR, complejo con React Query.
¿Cómo usar SWR con Next.js?
```tsx
// app/users/page.tsx (Server Component)
export default async function UsersPage() {
const initialUsers = await getUsers()
return <UsersList initialUsers={initialUsers} />
}
// components/UsersList.tsx (Client Component)
'use client'
export function UsersList({ initialUsers }) {
const { data } = useSWR('/api/users', fetcher, {
fallbackData: initialUsers
})
return <div>{data.map(...)}</div>
}
```
Ventajas:
• Primer pantallazo desde SSR
• Actualizaciones cliente vía SWR
• SSR y caché cliente combinados
Puntos clave:
• fallbackData y no initialData
• fallbackData no dispara revalidación
• Adecuado para App Router
¿Cuál es la estrategia de caché de SWR?
Flujo:
1. Primera petición: loading
2. Éxito: visualización y caché
3. Acceso siguiente: caché inmediata (rápido)
4. Segundo plano: revalidación (actualizado)
5. Terminado: actualización discreta (sin saltos)
Características:
• Caché compartida vía keys
• Deduplicación
• Revalidación al enfocar/reconectar
• Reintento ante errores
Opciones:
• revalidateOnFocus
• revalidateOnReconnect
• refreshInterval
• dedupingInterval
El usuario rara vez ve loading; los datos se mantienen frescos.
¿Cómo implementar actualizaciones optimistas?
```tsx
import { mutate } from 'swr'
async function updateUser(id: string, name: string) {
mutate(`/api/users/${id}`, { ...user, name }, false)
await fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify({ name })
})
mutate(`/api/users/${id}`)
}
```
Puntos clave:
• Tercer parámetro false = sin revalidación
• Tras el éxito, mutate de nuevo
• Actualización UI inmediata
Si falla: volver a los datos originales.
¿Para qué escenarios es adecuado SWR?
• Datos actualizados con frecuencia (listas, notificaciones)
• Varios componentes, mismos datos
• Caché y revalidación automáticas
• Proyectos Next.js
Menos adecuado:
• Datos estáticos puntuales
• Una sola petición sin revalidación
• Necesidades de caché muy complejas → React Query
Recomendación:
• La mayoría de proyectos → SWR
• Complejo → React Query
• Muy simple → fetch directo
Punto clave: no uses SWR por usar SWR — elige según la necesidad real.
14 min de lectura · Publicado el: 19 dic 2025 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
¿JWT o Session? Deja de dudar: después de leer esto lo entenderás
¿No sabes si elegir JWT o Session? Este artículo compara en profundidad ambas estrategias de sesión desde la práctica, con configuración de NextAuth.js, rendimiento y seguridad, para ayudarte a decidir bien.
Parte 49 de 51
Siguiente
Guía práctica de Drizzle ORM: un ORM TypeScript 90% más ligero que Prisma
Drizzle ORM pesa solo 7,4 kb, un 90% menos que Prisma, con arranques en frío un 73% más rápidos. Configuración Next.js + Drizzle, uso de la API SQL-like y comparativa de rendimiento con Prisma para elegir tu ORM TypeScript.
Parte 51 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario