# Trenutno stanje#
Next.js App Router promijenio je način na koji timovi razmišljaju o dohvaćanju podataka: React Server Components se izvršavaju na serveru, Client Components u pregledniku, a Next.js fetch dodaje vlastito cacheiranje i deduplikaciju povrh svega. To stvara čest problem: renderirate podatke na serveru, a zatim vaša biblioteka za client cache ponovno dohvaća iste podatke nakon hydracije.
Ovaj post uspoređuje React Query vs SWR u Next.js App Routeru s fokusom na stvarne produkcijske potrebe: ponašanje cachea, SSR i RSC kompatibilnost, mutacije, optimistična ažuriranja, developer experience te konkretne obrasce za izbjegavanje dvostrukog dohvaćanja.
ℹ️ Napomena: Ova usporedba pretpostavlja Next.js App Router s uključenim React Server Components te moderne verzije obje biblioteke. Za dublje osnove RSC-a pročitajte vodič za React Server Components. Za nijanse i kompromise oko Next.js cacheiranja pogledajte Next.js caching strategies SSR ISR SWR.
# Brza tablica usporedbe#
| Kriterij | React Query (TanStack Query) | SWR (Vercel) |
|---|---|---|
| Primarni model | Query cache s eksplicitnim ključevima i invalidacijom | Stale-while-revalidate cache po ključu s revalidacijom |
| Najbolji za | Složeni server state, dashboarde, SaaS CRUD, optimistična ažuriranja | Jednostavno read-heavy dohvaćanje, manje aplikacije, content widgete |
| Mutacije | Prvorazredni useMutation, retryji, invalidacijski pipelineovi | mutate i helperi za mutacije, više ručne orkestracije |
| Optimistična ažuriranja | Snažni, dobro dokumentirani obrasci | Moguća, ali obično uz više custom logike |
| RSC kompatibilnost | Koristi se u Client Components, hidrira sa servera | Koristi se u Client Components, daje fallback podatke |
| Izbjegavanje dvostrukog dohvaćanja | Prefetch na serveru i dehydrate ili proslijedi initialData | Proslijedi fallback kroz provider ili izbjegni server fetch za isti ključ |
| Devtools | Odličan | Minimalan |
| Ekosustav | Ogroman, multi-framework, snažni obrasci | Lean, manja površina API-ja |
| Krivulja učenja | Srednja | Niska |
| Utjecaj na bundle size | Veći | Manji |
# Kako cache stvarno radi u App Routeru#
Zbunjenost u App Routeru dolazi iz toga što je u igri više cache slojeva:
- 1Next.js
fetchcache na serveru: cacheira po requestu ili po route segmentu, ovisno ocacheinext.revalidate. - 2RSC render deduping: ponovljeni
fetchpozivi s istim inputom mogu se deduplicirati tijekom rendera. - 3Client cache: React Query ili SWR cacheiraju podatke u pregledniku kroz rendere, navigacije i refocus evente.
Dvostruko dohvaćanje se događa kada koristite i server fetch i client fetch za isti resurs, bez hidracije client cachea.
Scenarij dvostrukog dohvaćanja u jednoj rečenici#
Dohvatite na serveru za inicijalni HTML, a zatim se Client Component mounta i ponovno dohvaća jer je njegov cache prazan.
To nije samo potrošen bandwidth. Na sporijim mrežama može dodati 100 ms do 500 ms dodatne latencije i uzrokovati “flicker” UI-ja kada se podaci promijene između server rendera i client revalidacije. Za osnove performansi i profiliranje pogledajte website performance optimization.
# Modeli cacheiranja: React Query vs SWR#
React Query model cachea#
React Query tretira server state kao normalizirani cache indeksiran po query ključevima. Svježinu kontrolirate pomoću staleTime, a ponašanje refetcha na focus, reconnect i mount. Također dobivate eksplicitnu invalidaciju i ponovno dohvaćanje preko queryClient.invalidateQueries.
Praktične implikacije:
- Ako vaš dashboard ima 20 widgeta i dijele upite, React Query daje predvidljivu deduplikaciju i invalidaciju.
- Ako ažurirate jedan entitet, možete invalidirati povezane upite po djelomičnim ključevima i pustiti React Query da refetcha samo ono što je “stale”.
SWR model cachea#
SWR implementira stale-while-revalidate: odmah vraća cacheirane podatke, a zatim u pozadini radi revalidaciju. Namjerno je minimalističan: ključ, fetcher i kontrole revalidacije. Odličan je kada želite brzo, jednostavno client cacheiranje uz malo konfiguracije.
Praktične implikacije:
- Super za jednostavne stranice i male client widgete koji se mogu oportunistički revalidirati.
- Za složene grafove invalidacije obično završite tako da ručno pozivate
mutatepreko više ključeva ili pišete pomoćne utilse.
🎯 Ključni zaključak: React Query daje jače primitive za velike grafove upita i invalidaciju; SWR ostaje jednostavniji, ali s rastom kompleksnosti više orkestracije prebacuje na vašu aplikaciju.
# SSR i RSC kompatibilnost u Next.js App Routeru#
Ni React Query ni SWR ne bi se trebali koristiti izravno unutar Server Components. App Router očekuje da dohvaćate na serveru koristeći fetch ili server actions, pa zatim podatke proslijedite u Client Components.
Preporučeni mentalni model#
- Server Components: dohvat s Next.js
fetchi cacheiranjem route segmenata. - Client Components: React Query ili SWR za interaktivnost, pozadinski refetch, paginaciju, mutacije i optimistična ažuriranja.
- Izbjegavanje duplog dohvaćanja: hidrirajte client cache s podacima dohvaćenim na serveru.
React Query SSR hydration obrazac za App Router#
Najpouzdaniji pristup je: prefetch na serveru, dehydrate, pa hydrate u Client Provideru. Držite kod minimalnim i konzistentnim.
// app/providers.tsx
"use client";
import { QueryClient, QueryClientProvider, HydrationBoundary } from "@tanstack/react-query";
import { useState } from "react";
export function Providers(props: { state: unknown; children: React.ReactNode }) {
const [client] = useState(() => new QueryClient());
return (
<QueryClientProvider client={client}>
<HydrationBoundary state={props.state}>{props.children}</HydrationBoundary>
</QueryClientProvider>
);
}// app/page.tsx (Server Component)
import { dehydrate, QueryClient } from "@tanstack/react-query";
import { Providers } from "./providers";
async function getUser() {
const res = await fetch("https://api.example.com/me", { cache: "no-store" });
if (!res.ok) throw new Error("Failed");
return res.json();
}
export default async function Page() {
const qc = new QueryClient();
await qc.prefetchQuery({ queryKey: ["me"], queryFn: getUser });
const state = dehydrate(qc);
return (
<Providers state={state}>
{/* Client component uses useQuery(["me"]) without refetching */}
</Providers>
);
}Ključne točke:
- Vaš
queryFntreba pozivatifetchs ispravnom cache semantikom. - Preferirajte
cache: "no-store"za user-specific podatke ili strategiju revalidacije za javne podatke. - Postavite
staleTimeu client upitu kako biste izbjegli trenutni refetch na mountu.
SWR fallback obrazac za App Router#
SWR nudi fallback za seedanje cachea. Obrazac je sličan: dohvatite na serveru, proslijedite kao fallback, zatim koristite useSWR u Client Components s istim ključem.
// app/swr-provider.tsx
"use client";
import { SWRConfig } from "swr";
export function SWRProvider(props: { fallback: Record<string, unknown>; children: React.ReactNode }) {
return <SWRConfig value={{ fallback: props.fallback }}>{props.children}</SWRConfig>;
}// app/page.tsx (Server Component)
import { SWRProvider } from "./swr-provider";
async function getUser() {
const res = await fetch("https://api.example.com/me", { cache: "no-store" });
if (!res.ok) throw new Error("Failed");
return res.json();
}
export default async function Page() {
const user = await getUser();
return (
<SWRProvider fallback={{ "/api/me": user }}>
{/* Client component uses useSWR("/api/me") without refetching */}
</SWRProvider>
);
}Ključne točke:
- SWR ključ mora se točno podudarati, uključujući query string i konvencije base patha.
- Ako vaš client fetcher gađa drugi URL od server fetcha, i dalje ćete dobiti dvostruko dohvaćanje.
⚠️ Upozorenje: Najčešći double-fetch bug su nepodudarni ključevi. Server fetch na
https://api.example.com/mei client fetch na/api/mesu različiti cachevi i oba će se izvršiti ako ne ujednačite izvor ili ne seedate oba.
# Mutacije i optimistična ažuriranja#
Mutacije su mjesto gdje razlika postaje najvidljivija u stvarnim aplikacijama.
React Query mutacije#
React Query ima namjenski API za mutacije s jasnim lifecycleom:
onMutateza optimistična ažuriranjaonErrorza rollbackonSuccessza invalidaciju ili ažuriranje upitaonSettledza cleanup
Ovo je idealno za SaaS aplikacije s puno CRUD-a gdje je “perceived speed” važan. Česta produkt metrika: čak i 100 ms do 300 ms percipiranog poboljšanja tijekom učestalih akcija može smanjiti churn u internim alatima, jer UI djeluje responsivno.
Primjer obrasca optimističnog ažuriranja:
"use client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
export function useUpdateProfile() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (payload: { name: string }) => {
const res = await fetch("/api/profile", {
method: "PATCH",
headers: { "content-type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error("Failed");
return res.json();
},
onMutate: async (payload) => {
await qc.cancelQueries({ queryKey: ["me"] });
const prev = qc.getQueryData(["me"]);
qc.setQueryData(["me"], (old: any) => ({ ...old, name: payload.name }));
return { prev };
},
onError: (_err, _payload, ctx) => {
qc.setQueryData(["me"], ctx?.prev);
},
onSettled: () => {
qc.invalidateQueries({ queryKey: ["me"] });
},
});
}SWR mutacije#
SWR podržava mutacije preko mutate i helpera za mutacije, i možete raditi optimistična ažuriranja. Razlika je u tome što često pišete više “glue” koda za rollback i za ažuriranje više ključeva.
Čest pristup je:
mutate(key, updater, false)za lokalno ažuriranje bez revalidacije- izvršiti request
- revalidirati ili rollbackati
To je u redu za nekoliko endpointa. Postaje rizično kad imate više ovisnih cacheva, list i detail viewove te paginaciju.
# Developer experience i timski workflow#
React Query DX#
React Query obično pobjeđuje kada codebase naraste:
- Konvencije za query ključeve dobro skaliraju kroz timove.
- Devtools čine stanje cachea i razloge refetcha očitima.
- Obrasci invalidacije su konzistentni, što smanjuje bugove tijekom razvoja featurea.
Kompromis je učenje mentalnog modela: staleTime vs cacheTime, semantika invalidacije i hydration boundaryji.
SWR DX#
SWR često pobjeđuje po brzini isporuke:
- Vrlo malo setupa.
- Minimalno koncepata.
- Lako ga je “posuti” po malim Client Components.
Kompromis je što ćete sami izgraditi konvencije za ključeve, politike revalidacije i obrasce mutacija. Bez jakih konvencija, veći timovi završavaju s nekonzistentnim cache ponašanjem kroz aplikaciju.
💡 Savjet: Ako odaberete SWR za rastuću aplikaciju, rano definirajte jedan utility za izgradnju ključeva. Neka jedno mjesto slaže ključeve za list, detail i filtrirane varijante. To sprječava 80 posto slučajnih duplih ključeva i kasnijih invalidacija.
# Izbjegavanje dvostrukog dohvaćanja: provjereni obrasci#
Dvostruko dohvaćanje rijetko je bug biblioteke. Gotovo uvijek je arhitekturni nesrazmjer.
Obrazac A: Server fetch za first paint, hidracija client cachea#
Koristite kada:
- SEO ili brz first paint je bitan.
- Isti podaci se odmah koriste u Client Componentu.
- Želite interaktivnost nakon hydracije bez refetcha.
React Query: prefetchQuery + dehydrate + HydrationBoundary.
SWR: server fetch + SWRConfig fallback.
Obrazac B: Isključivo client fetch, bez server fetcha za te podatke#
Koristite kada:
- Podaci su user-specific i nisu potrebni za inicijalni HTML.
- Želite izbjeći server load i zadržati rutu statičnom.
- Možete prikazati skeleton ili placeholder.
Ovo je često za dashboarde koji brzo renderiraju okvir, a zatim postupno učitavaju widgete.
Obrazac C: Neka Next.js cache upravlja javnim sadržajem, client cache koristite štedljivo#
Za content stranice:
- Dohvaćajte sadržaj u Server Components koristeći
next.revalidate. - Izbjegavajte client caching biblioteke osim ako imate interaktivne widgete kojima treba client-side revalidacija.
Time smanjujete bundle size i posao oko hydracije. Također se bolje uklapa u prednosti App Routera.
Debug checklist za duple zahtjeve#
- 1Potvrdite je li prvi request server-side, a drugi client-side.
- 2Provjerite jednakost ključeva: identičan SWR key string ili React Query queryKey array.
- 3Provjerite jednakost URL-ova: isti path i query string, iste pretpostavke o base URL-u.
- 4Postavite
staleTimeu React Queryju da spriječite trenutni refetch nakon hydracije. - 5U SWR-u isključite
revalidateOnMountkada stvarno želite vjerovati fallbacku neko vrijeme.
# Preporuke prema tipu aplikacije#
Dashboardi i interni alati#
Tipične značajke: puno widgeta, filteri, paginacija, česte mutacije i podaci koji se često mijenjaju.
Preporuka:
- Preferirajte React Query zbog predvidljivih query grafova, deduplikacije i invalidacije.
- Server rendering koristite za layout i samo kritične sažetke, a zatim hidrirajte widgete ili ih učitavajte client-only.
Zašto je važno:
- Dashboardi često pokreću desetke requestova. React Query cache sprječava ponovljene refetcheve kada korisnici navigiraju između tabova ili otvaraju detail drawerove.
SaaS proizvodi s CRUD-om i “real-time-ish” UX-om#
Tipične značajke: liste i detail viewovi, forme, optimistična ažuriranja, višekoračni flowovi i visoka UX očekivanja.
Preporuka:
- Preferirajte React Query za mutacije i optimistična ažuriranja.
invalidateQuerieskoristite strateški umjesto globalnog refetcha.- Razmislite o kombinaciji server actions za write operacije i React Queryja za ažuriranje client cachea.
Zašto je važno:
- Optimistična ažuriranja smanjuju percipiranu latenciju i čine proizvod bržim. React Query daje sigurnije obrasce za rollback.
Content stranice i marketing#
Tipične značajke: većinom read-only sadržaj, SEO-driven, relativno stabilni podaci, malo client interakcije.
Preporuka:
- Preferirajte SWR samo za male client widgete poput provjere statusa newslettera, dostupnosti cijena ili personalization flagova.
- Za glavni sadržaj koristite Server Components i Next.js caching i u mnogim slučajevima preskočite client caching biblioteke.
Zašto je važno:
- Na content stranicama bundle size i vrijeme hydracije važniji su od sofisticiranog client cacheiranja.
# Savjeti za migraciju: SWR → React Query i React Query → SWR#
Migracija je manje zamjena hookova, a više usvajanje modela cacheiranja.
Migracija sa SWR-a na React Query#
Što se mijenja:
- Ključevi idu od proizvoljnih stringova prema strukturiranim query ključevima, obično arrayevima.
- Revalidacija postaje invalidacija i refetching, što je eksplicitnije.
- Mutacije postaju
useMutations lifecycle handlerima.
Praktični koraci:
- 1Kreirajte konvenciju query ključeva, npr.
["users", "list", filters]i["users", "detail", id]. - 2Wrapajte aplikaciju u jedan QueryClientProvider u
app/providers.tsx. - 3Prvo zamijenite kritične SWR hookove, počevši od endpointa koji uzrokuju najviše dvostrukog dohvaćanja ili nekonzistentnih ažuriranja.
- 4Zamijenite SWR
mutatepozive suseMutation+invalidateQueries.
Predloženo mapiranje:
| Koncept | SWR | React Query |
|---|---|---|
| Hook za čitanje | useSWR(key, fetcher) | useQuery({ queryKey, queryFn }) |
| Globalno ažuriranje cachea | mutate(key) | queryClient.invalidateQueries({ queryKey }) |
| Optimistična ažuriranja | mutate(key, data, false) | onMutate + setQueryData |
| Prefill sa servera | SWRConfig fallback | dehydrate + HydrationBoundary |
Migracija s React Queryja na SWR#
To se obično događa kada:
- Aplikacija je read-heavy i želite manje apstrakcije.
- Želite smanjiti bundle size i mentalni overhead.
- Rijetko imate mutacije ili ih možete izolirano riješiti.
Praktični koraci:
- 1Auditirajte korištenje query ključeva i pojednostavite u stabilne string ključeve.
- 2Zamijenite invalidacijske obrasce s eksplicitnim
mutatepozivima na relevantnim ključevima. - 3Ponovno procijenite stale politike: SWR defaulti mogu revalidirati češće nego što očekujete.
- 4Ako ste se oslanjali na Devtools za debugiranje, dodajte lagani logging oko fetchera i mutation flowova.
⚠️ Upozorenje: Ako vaša aplikacija ovisi o složenoj invalidaciji, prelazak s React Queryja na SWR može povećati rizik od bugova. Timovi često promaše jedan od povezanih ključeva, pa UI ostane stale u edge flowovima poput bulk edita ili paginacije.
# Cijena, održavanje i uklapanje u ekosustav#
Obje biblioteke su zrele i široko korištene.
- SWR održava Vercel i dobro se uklapa u Next.js ekosustav.
- React Query je dio TanStacka i široko se koristi u Reactu i šire.
Iz perspektive rizika, obje su siguran izbor. Odlučujući faktori su kompleksnost aplikacije i koliko želite da biblioteka upravlja za vas.
# Ključni zaključci#
- Izbjegnite dvostruko dohvaćanje tako da dohvatite jednom i hidrirate client cache: React Query koristi dehydrate i HydrationBoundary, SWR koristi SWRConfig fallback.
- Odaberite React Query za dashboarde i SaaS aplikacije s čestim mutacijama, složenom invalidacijom i optimističnim ažuriranjima.
- Odaberite SWR za jednostavno read-heavy client dohvaćanje ili male interaktivne widgete unutar content-first Next.js stranica.
- Ujednačite cache ključeve i URL-ove između servera i klijenta, inače hidracija neće spriječiti duple requestove.
- U App Routeru tretirajte Server Components i Next.js fetch caching kao primarni mehanizam za inicijalne podatke, a client caching biblioteke koristite za interaktivnost nakon hydracije.
# Zaključak#
React Query i SWR oba rade u Next.js App Routeru, ali optimizirani su za različite stvarnosti. Ako je vaša aplikacija “mutation-heavy” ili ima složen query graf, React Query će uštedjeti inženjersko vrijeme i smanjiti bugove sa stale UI-jem. Ako su potrebe većinom read-only i želite najjednostavniji alat koji radi, SWR je često brži put.
Ako želite pomoć oko odabira pristupa, rješavanja dvostrukog dohvaćanja ili postavljanja skalabilne caching strategije za App Router, kontaktirajte Samioda i pregledat ćemo vaš trenutni data flow te predložiti konkretan plan implementacije.
FAQ
Više iz kategorije Web razvoj
Sve →Next.js učitavanje datoteka kako treba: izravno na S3 i Cloudflare R2 s presigned URL-ovima, validacijom i sigurnošću
Praktičan vodič za 2026. o izradi sigurnih i pouzdanih izravnih uploadova u object storage u Next.js App Routeru uz presigned URL-ove, serversku validaciju, rukovanje ponovnim pokušajima i opcionalno antivirusno skeniranje.
Next.js pozadinski poslovi u 2026.: redovi, Cron i dugotrajni zadaci na Vercelu (i šire)
Praktičan vodič za izvođenje pozadinskog rada u Next.js-u u 2026.: Vercel Cron, ograničenja serverlessa, redovi s Upstashom i Redisom te worker servisi za dugotrajne zadatke. Uključuje kriterije odlučivanja, arhitekturne dijagrame i checklistu za produkciju.
Next.js i18n s App Routerom: lokalizirano rutiranje, SEO i workflowi za sadržaj (vodič za 2026.)
Implementirajte Next.js i18n u App Routeru s lokaliziranim rutiranjem, detekcijom jezika, SEO-sigurnim metapodacima i skalabilnim workflowima prevođenja za JSON, CMS ili lokalizacijske platforme.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Objašnjene strategije cacheiranja u Next.js-u: SSR, SSG, ISR, Route Cache i SWR
Praktičan vodič kroz Next.js strategije cacheiranja u eri App Routera — kako se SSR, SSG, ISR, Route Cache, Data Cache i SWR uklapaju u cjelinu, uz tablice za odluke, primjere koda i česte zamke poput zastarjelih auth i tenant podataka.
Kontrolni popis za migraciju na Next.js App Router (s Pages Routera) + česte zamke
Praktičan, korak-po-korak plan migracije na Next.js App Router s Pages Routera, uključujući kontrolni popis za routing, dohvat podataka, SEO metadata, deployment i vodič za rješavanje čestih zamki.
Next.js učitavanje datoteka kako treba: izravno na S3 i Cloudflare R2 s presigned URL-ovima, validacijom i sigurnošću
Praktičan vodič za 2026. o izradi sigurnih i pouzdanih izravnih uploadova u object storage u Next.js App Routeru uz presigned URL-ove, serversku validaciju, rukovanje ponovnim pokušajima i opcionalno antivirusno skeniranje.