React Query vs SWR: Mana yang Digunakan di 2026

Foto oleh Unsplash

Foto oleh Unsplash
Baik React Query (kini TanStack Query) maupun SWR memecahkan masalah yang sama: mengelola remote state di React tanpa prop drilling dan spaghetti useEffect yang dihasilkan dari panggilan fetch manual. Saya telah menggunakan keduanya di proyek produksi. SWR menggerakkan situs Next.js publik yang lebih sederhana; React Query menjalankan dashboard untuk alat ERP internal dengan mutasi kompleks, pembaruan optimis, dan dukungan offline.
SWR (Stale-While-Revalidate) adalah dari Vercel dan dioptimalkan untuk ekosistem Vercel/Next.js. Filosofinya adalah surface API minimal — satu hook useSWR mencakup sebagian besar kasus penggunaan. React Query dari TanStack lebih beropini tentang manajemen server state, dilengkapi dengan DevTools, dan memiliki dukungan kelas pertama untuk mutasi, pagination, infinite query, dan mode offline.
Kedua library mendeduplikasi request — jika tiga komponen di halaman yang sama memanggil useSWR('/api/user') atau useQuery(['user']), hanya satu request HTTP yang dikirim. Keduanya melakukan revalidasi saat window fokus dan reconnect. Keduanya mendukung background refetching, fungsi fetcher kustom, dan konfigurasi global.
SWR menang untuk situs berat konten di mana kamu terutama perlu mengambil dan menampilkan data dengan mutasi sederhana sesekali. API-nya lebih kecil untuk dipelajari, terintegrasi sempurna dengan ekosistem Vercel, dan untuk CRUD sederhana tanpa kebutuhan invalidasi cache yang kompleks, ergonomisnya lebih bersih.
Feature Comparison: React Query vs SWR
┌─────────────────────────┬──────────────┬──────────────┐
│ Feature │ React Query │ SWR │
├─────────────────────────┼──────────────┼──────────────┤
│ Bundle size (min+gzip) │ ~13.5 kB │ ~4.2 kB │
│ Mutations │ ✅ Built-in │ ⚠️ Manual │
│ Optimistic updates │ ✅ Easy │ ✅ Possible │
│ Infinite scroll │ ✅ Built-in │ ✅ Built-in │
│ Offline support │ ✅ Yes │ ⚠️ Partial │
│ DevTools │ ✅ Official │ ❌ None │
│ Server State (SSR) │ ✅ Full │ ✅ Full │
│ Background refetch │ ✅ Yes │ ✅ Yes │
│ Request deduplication │ ✅ Yes │ ✅ Yes │
│ Prefetching │ ✅ Yes │ ⚠️ Manual │
│ Learning curve │ Medium │ Low │
│ Best for │ Complex apps │ Simple fetch │
└─────────────────────────┴──────────────┴──────────────┘Di Next.js App Router, prefetch data sisi server menggunakan QueryClient.prefetchQuery() di Server Component-mu, kemudian teruskan state yang di-dehydrate ke HydrationBoundary di Client Component. Ini memberimu zero-loading-state pada render pertama sambil tetap mendapatkan semua manajemen cache sisi klien React Query.
Mutasi SWR memerlukan pembaruan cache manual melalui fungsi mutate() — kamu memberi tahu SWR apa nilai cache baru seharusnya. Hook useMutation React Query memiliki siklus hidup onMutate / onError / onSettled yang membuat pembaruan optimis, rollback pada error, dan invalidasi cache menjadi pola terstruktur daripada logika ad-hoc.
Kedua library mendukung infinite scrolling dengan useInfiniteQuery (React Query) dan useSWRInfinite (SWR). Implementasi React Query lebih ergonomis — array pages dan fungsi fetchNextPage() menangani sebagian besar pola pagination tanpa logika kustom. SWR memerlukan pengkabelan manual lebih banyak tapi memberimu lebih banyak kontrol atas strategi penggabungan data.
// ─── React Query: full mutation with optimistic update ───
import { useMutation, useQueryClient } from "@tanstack/react-query"
function useUpdateProduct() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: UpdateProductDto) =>
fetch(`/api/products/${data.id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}).then((r) => r.json()),
// Optimistic update
onMutate: async (newProduct) => {
await queryClient.cancelQueries({ queryKey: ["products", newProduct.id] })
const prev = queryClient.getQueryData(["products", newProduct.id])
queryClient.setQueryData(["products", newProduct.id], newProduct)
return { prev }
},
onError: (_err, newProduct, ctx) => {
queryClient.setQueryData(["products", newProduct.id], ctx?.prev)
},
onSettled: (_data, _err, vars) => {
queryClient.invalidateQueries({ queryKey: ["products", vars.id] })
queryClient.invalidateQueries({ queryKey: ["products"] })
},
})
}
// ─── SWR: fetching + manual mutation ───
import useSWR, { mutate } from "swr"
const fetcher = (url: string) => fetch(url).then((r) => r.json())
function useProducts() {
const { data, error, isLoading } = useSWR("/api/products", fetcher, {
revalidateOnFocus: true,
dedupingInterval: 5000,
})
const updateProduct = async (id: string, payload: Partial<Product>) => {
// Optimistic update with SWR
await mutate(
"/api/products",
async (current: Product[]) => {
const updated = await fetch(`/api/products/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}).then((r) => r.json())
return current.map((p) => (p.id === id ? updated : p))
},
{ optimisticData: (current) =>
current?.map((p) => (p.id === id ? { ...p, ...payload } : p)) }
)
}
return { data, error, isLoading, updateProduct }
}Wawasan kunci yang dibagikan kedua library: server state secara fundamental berbeda dari client state. Client state (toggle UI, input form, modal buka/tutup) milik useState atau state manager seperti Zustand. Server state (record database, respons API) milik React Query atau SWR. Mencampurnya menyebabkan data basi, race condition, dan bug sinkronisasi.
Menggunakan kedua library dalam codebase yang sama menggandakan biaya bundle dan menciptakan dua layer cache terpisah yang tidak saling mengetahui. Pilih satu saat awal proyek dan pertahankan. Jika kamu mewarisi codebase dengan keduanya, migrasikan semuanya ke React Query (lebih capable) dan hapus SWR.
Di Next.js App Router, Server Component menangani pengambilan awal — tidak perlu React Query atau SWR untuk first paint. React Query dan SWR masuk untuk interaksi sisi klien setelah hidrasi: merefresh data pada aksi pengguna, polling untuk pembaruan langsung, dan mutasi. Bungkus Client Component-mu dalam QueryClientProvider atau SWRConfig di root layout-mu.
Untuk proyek Next.js baru: mulai dengan SWR. Ini mencakup 80% kasus penggunaan dengan bundle lebih kecil dan model mental yang lebih sederhana. Migrasi ke React Query jika kamu membutuhkan DevTools untuk men-debug cache state, melakukan mutasi optimis yang memerlukan rollback, memiliki dependent query yang kompleks, atau membutuhkan perilaku offline-first. Untuk dashboard ERP, panel admin, dan aplikasi berat data: mulai dengan React Query.