# Što ćete naučiti#
React Server Components (često se pretražuje kao react server components) jedan su od najvećih pomaka u modernoj React arhitekturi: omogućuju da dijelove UI-ja renderirate na serveru bez slanja njihovog JavaScripta u browser. To mijenja način na koji razmišljate o performansama, dohvaćanju podataka i granicama između komponenti.
Ovaj vodič objašnjava RSC koncepte jednostavnim jezikom, pokazuje kada koristiti Server vs Client Components i prolazi kroz praktične primjere u Next.js App Routeru (danas najčešća produkcijska implementacija). Ako ste novi u App Routeru, krenite s osnovnim uvodom: Kako započeti s Next.js.
# Preduvjeti#
| Zahtjev | Verzija | Napomena |
|---|---|---|
| Node.js | 18+ (20+ preporučeno) | Potrebno za Next.js dev/build |
| Next.js | 14+ | App Router + RSC podrška |
| React | 18+ | RSC je mogućnost iz ere Reacta 18 |
| Osnove Reacta | — | Komponente, props, uvjetno renderiranje |
| Osnove HTTP/dohvata podataka | — | REST/GraphQL koncepti pomažu |
ℹ️ Napomena: React Server Components su React značajka, ali Next.js App Router je mjesto gdje ih većina timova danas koristi. Mentalni model koji ovdje usvojite prenosi se i na druge frameworke koji podržavaju RSC.
# React Server Components: mentalni model#
Dobar način da razumijete RSC je da odvojite gdje se komponenta izvršava od onoga što korisnik vidi.
Što je Server Component?#
Server Component je React komponenta koja se izvršava isključivo na serveru. Njezin se izlaz koristi za izgradnju UI-ja, ali se njezin kod ne isporučuje u browser.
Ta jedna osobina ima dvije velike posljedice:
- 1Možete sigurno pristupati resursima koji postoje samo na serveru (DB, tajne, interne usluge).
- 2Smanjujete količinu JavaScripta koju browser mora preuzeti, parsirati i izvršiti.
Što je Client Component?#
Client Component se izvršava u browseru. Koristi se kada trebate interaktivnost: state, efekte, event handlere i browser API-je.
U Next.js App Routeru u Client Component ulazite tako da na vrh datoteke dodate direktivu "use client".
RSC nije SSR (i nije “samo API + HTML”)#
SSR (Server-Side Rendering) generira HTML na serveru za početno učitavanje. RSC proizvodi server-renderano stablo komponenti koje se može streamati i kompozicijski slagati, te omogućuje obrazac u kojem logika dohvaćanja podataka i renderiranja može živjeti “uz” vaše komponente bez izlaganja tog koda browseru.
Praktično, u Next.js App Routeru obično kombinirate:
- Server Components za dohvat podataka + renderiranje
- Client Components za interaktivne otoke (filteri, gumbi, forme)
🎯 Ključna poruka: Uz RSC, kao zadano koristite Server Components za renderiranje i pristup podacima, a na Client Components “prelazite” samo ondje gdje to zahtijeva korisnička interakcija.
# Zašto su React Server Components važni (benefiti koje možete mjeriti)#
RSC je primarno značajka performansi i arhitekture, a ne sintakse. Ovo su benefiti koje timovi najčešće primijete u produkciji.
1) Manji client bundle (manje JS-a se isporučuje)#
Budući da se Server Components nikad ne izvršavaju na klijentu, njihov kod nije uključen u browser bundle. To smanjuje:
- veličinu preuzimanja (posebno na mobitelu)
- vrijeme parsiranja/kompajliranja
- izvršavanje JS-a na main threadu
To je važno jer performanse kod stvarnih korisnika često ograničava JS, a ne HTML. Primjerice, Googleovi Web Vitals naglašavaju responsivnost (INP) i stabilnost renderiranja (CLS). Manje isporučenog JavaScripta tipično pomaže smanjiti “long tasks” i poboljšati latenciju interakcije.
2) Jednostavniji “secure by default” pristup podacima#
Server Components mogu pristupati tajnama i internim mrežama bez curenja u browser. To uklanja cijelu klasu grešaka tipa “ups, isporučili smo API ključ”.
Primjeri sigurnog server-only rada:
- upiti prema Postgres/MySQL
- pozivanje internih microservicea
- korištenje
process.env.*tajni - potpisivanje zahtjeva
- generiranje sigurnih tokena
3) Bolja ergonomija dohvaćanja podataka (bliže UI-ju)#
Umjesto izgradnje zasebnog “data layera” koji zrcali vaše stablo komponenti, RSC potiče dohvat ondje gdje renderirate, a da pritom sve ostaje na serveru.
U Next.js možete await-ati podatke direktno u Server Components, a zatim rezultate proslijediti dalje kao props.
4) Streaming + Suspense za brže percipirano učitavanje#
RSC odlično radi sa streamingom. Možete brzo renderirati “shell” UI, a zatim streamati sporije segmente kako se razrješavaju (liste proizvoda, preporuke, komentari).
To poboljšava percipirane performanse, posebno na sporim vezama ili kada gađate više backenda.
💡 Savjet: Ako radite eCommerce ili content-heavy stranicu, česta pobjeda je: renderirati header + naslov proizvoda odmah, a zatim streamati “slične proizvode” i “recenzije” iza
<Suspense>granica.
# Server vs Client Components: okvir za odluku#
Koristite ovu tablicu da odlučite gdje komponenta treba živjeti.
| Zahtjev | Preferiraj Server Component | Preferiraj Client Component |
|---|---|---|
Treba useState / useEffect / useReducer | ✗ | ✓ |
Treba event handlere (onClick, onSubmit) | ✗ | ✓ |
Koristi browser API-je (window, document, localStorage) | ✗ | ✓ |
| Dohvaća podatke uz tajne / DB pristup | ✓ | ✗ |
| Teško renderiranje / transformacija podataka | ✓ | ✗ (osim ako treba za UX) |
| SEO-kritičan sadržaj | ✓ | Ponekad (ali obično server) |
| Ponovno iskoristiv UI widget (čisto prezentacijski) | ✓ | ✓ (ovisno o interaktivnosti) |
| Koristi third-party client-only biblioteke (charts, maps) | ✗ | ✓ |
Praktično pravilo koje radi za većinu timova:
- Server: stranice, layouti, komponente za učitavanje podataka, sekcije sadržaja, liste
- Client: inputi, togglei, modali, toastovi, kompleksne interakcije
⚠️ Upozorenje: Client Component granica povlači svu importanu djecu u client bundle. Ako dodate
"use client"previsoko u stablu (npr. ulayout.tsx), možete slučajno prisiliti velike dijelove aplikacije da postanu client-side.
# Next.js App Router: RSC kao zadano ponašanje#
U Next.js direktoriju app/:
page.tsxdatoteke su Server Components po defaultulayout.tsxdatoteke su Server Components po defaultu- Client ponašanje uključujete pomoću
"use client"
To potiče arhitekturu u kojoj se većina renderiranja događa na serveru, a samo interaktivni dijelovi postaju client “otoci”.
Primjer strukture projekta#
| Putanja | Tipična uloga | Server/Client |
|---|---|---|
app/products/page.tsx | Ulaz rute, dohvat podataka | Server |
app/products/ProductGrid.tsx | Render liste proizvoda | Server |
app/products/Filters.tsx | Interaktivni UI za filtere | Client |
app/products/ProductCard.tsx | Prezentacijska kartica stavke | Server (osim ako je interaktivna) |
# Korak 1: Napravite Server Component stranicu koja dohvaća podatke#
Ovaj primjer pokazuje Server Component page.tsx koji dohvaća proizvode i renderira ih. U stvarnim aplikacijama koristili biste DB ili interni API; ovdje ćemo koristiti placeholder endpoint.
// app/products/page.tsx
type Product = { id: string; name: string; price: number };
async function getProducts(): Promise<Product[]> {
const res = await fetch("https://example.com/api/products", {
// In Next.js, fetch is server-aware; use caching intentionally.
cache: "no-store",
});
if (!res.ok) throw new Error("Failed to fetch products");
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<main>
<h1>Products</h1>
<ul>
{products.map((p) => (
<li key={p.id}>
{p.name} — €{p.price}
</li>
))}
</ul>
</main>
);
}Zašto je ovo bitno: browser dobiva HTML (i RSC payload), ali ne dobiva kod za getProducts() niti vaše server tajne (ako ih ima). To je čista separacija između UI-ja i pristupa podacima.
# Korak 2: Dodajte Client Component za interaktivnost (Filteri)#
Filtriranje je interaktivno: treba state i event handlere. Držite ga na klijentu, ali nemojte cijelu stranicu prebaciti na klijent.
Client komponenta: Filters.tsx#
// app/products/Filters.tsx
"use client";
import { useMemo, useState } from "react";
type Props = {
categories: string[];
onChange: (category: string | null) => void;
};
export default function Filters({ categories, onChange }: Props) {
const [selected, setSelected] = useState<string | null>(null);
const options = useMemo(() => ["All", ...categories], [categories]);
return (
<div>
{options.map((c) => {
const value = c === "All" ? null : c;
return (
<button
key={c}
onClick={() => {
setSelected(value);
onChange(value);
}}
aria-pressed={selected === value}
>
{c}
</button>
);
})}
</div>
);
}Server komponenta stranice koristi client komponentu#
U App Routeru, Server Component može renderirati Client Component i proslijediti serijalizabilne props (stringove, brojeve, nizove, plain objekte).
// app/products/page.tsx
import Filters from "./Filters";
export default async function ProductsPage() {
const categories = ["Shoes", "T-Shirts", "Accessories"];
return (
<main>
<h1>Products</h1>
<Filters
categories={categories}
onChange={() => {
// You can't pass functions from server to client.
// We'll handle this properly in the next step.
}}
/>
</main>
);
}Ovo još neće raditi jer ne možete proslijediti funkciju iz Server Componenta u Client Component.
Pa kako povezati filtere sa server-side podacima? Postoje tri uobičajena obrasca:
- 1URL search params (preporučeno za liste koje se mogu dijeliti i filtrirati)
- 2Server Actions (dobro za mutacije)
- 3Client-side fetching (samo kada je stvarno potrebno)
Sljedeće ćemo implementirati URL obrazac.
# Korak 3: Koristite URL Search Params kao most između client UI-ja i server podataka#
Kad filteri ažuriraju URL, ruta se ponovno renderira na serveru s novim searchParams. Tako dohvat podataka ostaje na serveru, a stranica je “shareable” i “bookmarkable”.
Client komponenta ažurira URL#
// app/products/Filters.tsx
"use client";
import { useRouter, useSearchParams } from "next/navigation";
type Props = { categories: string[] };
export default function Filters({ categories }: Props) {
const router = useRouter();
const params = useSearchParams();
const active = params.get("category");
return (
<div>
<button
onClick={() => router.push("/products")}
aria-pressed={!active}
>
All
</button>
{categories.map((c) => (
<button
key={c}
onClick={() => router.push(`/products?category=${encodeURIComponent(c)}`)}
aria-pressed={active === c}
>
{c}
</button>
))}
</div>
);
}Server komponenta koristi searchParams za server-side filtriranje#
// app/products/page.tsx
type Product = { id: string; name: string; price: number; category: string };
async function getProducts(category?: string | null): Promise<Product[]> {
const url = new URL("https://example.com/api/products");
if (category) url.searchParams.set("category", category);
const res = await fetch(url.toString(), { cache: "no-store" });
if (!res.ok) throw new Error("Failed to fetch products");
return res.json();
}
export default async function ProductsPage({
searchParams,
}: {
searchParams: Promise<{ category?: string }>;
}) {
const { category } = await searchParams;
const products = await getProducts(category);
return (
<main>
<h1>Products</h1>
<p>Category: {category ?? "All"}</p>
<ul>
{products.map((p) => (
<li key={p.id}>
{p.name} — €{p.price}
</li>
))}
</ul>
</main>
);
}Zašto je ovo bitno: dobivate interaktivnost bez odustajanja od server-side dohvaćanja podataka, i izbjegavate isporučivanje vaše logike dohvaćanja na klijent.
# Korak 4: Koristite Suspense + Streaming za “teške” sekcije#
RSC dolazi do izražaja kada spore dijelove stranice podijelite u sekcije koje se učitavaju neovisno. U Next.js App Routeru to možete napraviti pomoću Suspense.
Primjer: streamajte preporuke odvojeno#
// app/products/Recommendations.tsx
type Product = { id: string; name: string };
async function getRecommendations(): Promise<Product[]> {
const res = await fetch("https://example.com/api/recommendations", {
cache: "no-store",
});
if (!res.ok) throw new Error("Failed to fetch recommendations");
return res.json();
}
export default async function Recommendations() {
const items = await getRecommendations();
return (
<aside>
<h2>Recommended</h2>
<ul>
{items.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
</aside>
);
}// app/products/page.tsx
import { Suspense } from "react";
import Recommendations from "./Recommendations";
export default async function ProductsPage() {
return (
<main>
<h1>Products</h1>
<Suspense fallback={<p>Loading recommendations…</p>}>
<Recommendations />
</Suspense>
</main>
);
}Ovo je važno jer korisnici mogu početi čitati i koristiti glavni sadržaj dok se sekundarni sadržaj streama, što često poboljšava percipiranu brzinu na sporijim mrežama.
💡 Savjet: Najsporije backend pozive stavite iza Suspense granice, a sadržaj iznad pregiba (above-the-fold) držite u brzoj Server Componenti. Ovo obično poboljšava realni engagement više nego mikro-optimizacija client-side koda.
# Korak 5: Server Actions (kada trebate mutacije)#
Filtriranje je najbolje raditi preko URL parametara, ali slanje formi i mutacije često su čišće uz Server Actions. Omogućuju da pokrenete server kod iz forme bez izrade zasebne API rute.
Primjer: add-to-wishlist action#
// app/products/actions.ts
"use server";
export async function addToWishlist(productId: string) {
// Safe: server-only logic (DB call, internal API, auth checks)
await fetch("https://example.com/api/wishlist", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ productId }),
});
}// app/products/AddToWishlistButton.tsx
"use client";
import { useTransition } from "react";
import { addToWishlist } from "./actions";
export default function AddToWishlistButton({ productId }: { productId: string }) {
const [pending, startTransition] = useTransition();
return (
<button
disabled={pending}
onClick={() => startTransition(() => addToWishlist(productId))}
>
{pending ? "Saving…" : "Add to wishlist"}
</button>
);
}Zašto je ovo bitno: logika mutacije ostaje na serveru, ne isporučujete credentiale i smanjujete API boilerplate. Koristite ovo za write operacije: create/update/delete, pretplate, kontakt forme itd.
⚠️ Upozorenje: Nemojte tretirati Server Actions kao “besplatnu sigurnost”. I dalje trebate autentikaciju, autorizaciju i validaciju inputa na serveru, isto kao i za API endpoint.
# Česta RSC ograničenja (i kako dizajnirati oko njih)#
RSC je moćan, ali ima oštre rubove. Većina produkcijskih problema dolazi iz pogrešnog razumijevanja ograničenja.
1) Ne možete koristiti hookove u Server Components#
Hookovi poput useState, useEffect i useRef zahtijevaju browser runtime. Ako ih trebate, izolirajte taj dio u Client Component.
2) Ne možete proslijediti funkcije iz Server u Client Components#
Server Components mogu prosljeđivati samo serijalizabilne props. Umjesto toga koristite jedan od ovih obrazaca:
- URL parametre za filtriranje/sortiranje/paginaciju
- Server Actions za mutacije
- Client fetch za potpuno client-driven UX (zadnja opcija)
3) Client granice mogu slučajno napuhati bundle#
"use client" na krivom mjestu može pretvoriti velike dijelove u client kod. Držite client komponente male i što bliže listovima (leaf-level).
4) Third-party biblioteke mogu prisiliti Client Components#
Mnoge UI biblioteke ovise o browser API-jima. Omotajte ih u male client komponente i u njih proslijedite server-dohvaćene podatke kao props.
# Praktičan arhitekturni obrazac: “Server shell + Client islands”#
Ovo je obrazac koji dobro radi za dashboarde, marketplaceove i SaaS:
- 1Stranica i glavne sekcije su Server Components
- 2Svaki interaktivni widget je Client Component
- 3Dohvat podataka se radi na serveru, zatim se prosljeđuje dolje
| Dio stranice | Primjer | Tip komponente |
|---|---|---|
| Header, navigacija, breadcrumbs | Statični ili user-aware UI | Server |
| Redovi tablice podataka | 100–5.000 redova server-renderano s paginacijom | Server |
| UI za sortiranje stupaca | Grupa gumba, dropdown | Client |
| Akcije po retku | “Edit”, “Archive”, modal | Client |
| Mutacije | Create/update/delete | Server Actions |
Ovo je važno jer možete zadržati nizak Time to Interactive, a istovremeno isporučiti bogat UX.
Ako gradite React/Next.js proizvod i želite da se ovaj obrazac implementira čisto uz performance budžete i analitiku, upravo to radimo u Samioda: web & mobile development.
# Caching i revalidacija: nemojte si slučajno napraviti DDOS#
Uz App Router, fetch() se integrira s Nextovim cachingom i može se revalidirati. Trebate svjesnu strategiju, inače ćete ili posluživati zastario sadržaj ili preopteretiti backend.
Uobičajeni caching modovi u Next.js (konceptualno)#
| Use case | Preporučena postavka | Zašto |
|---|---|---|
| Uvijek svježi podaci (cijene, zalihe) | cache: "no-store" | Izbjegnite stale rezultate |
| Uglavnom statičan sadržaj (blog, dokumentacija) | default caching ili revalidate | Smanjite opterećenje backenda |
| “Dovoljno svježi” podaci (ažuriranja kataloga) | revalidate svakih X sekundi | Balans svježine i troška |
Jednostavno pravilo: ako prikaz zastarjelih podataka može naštetiti korisniku (kriva cijena, kriva dostupnost), kao zadano koristite no-store ili kratku revalidaciju.
# Česte zamke (i kako ih izbjeći)#
- 1Označavanje cijelih ruta kao client — Držite
"use client"na najmanjoj mogućoj granici komponente kako biste izbjegli bloat bundlea. - 2Dohvat na klijentu kao default — Preferirajte server dohvat za inicijalni render; na klijent prebacite samo za UX koji se uživo ažurira.
- 3Pokušaj korištenja browser API-ja u Server Components — Ako trebate
localStorageiliwindow, izolirajte tu logiku u Client Component. - 4Ignoriranje streaming prilika — Koristite
<Suspense>oko sporih sekcija poput preporuka, recenzija ili analytics widgeta. - 5Miješanje logike mutacija u client kod — Preferirajte Server Actions za write operacije kako bi sigurnosne provjere ostale na serveru.
Za dobru App Router osnovu prije refaktora prema RSC-first obrascima, pogledajte: Kako započeti s Next.js.
# Ključne poruke#
- Kao zadano koristite Server Components za dohvat podataka, renderiranje i siguran pristup tajnama/DB-u; isporučite manje JavaScripta u browser.
- Client Components koristite samo za interaktivnost (state, efekti, handleri) i držite ih malima kako biste kontrolirali veličinu bundlea.
- Povežite client UI sa server podacima pomoću URL search params za filtre/sortiranje/paginaciju; koristite Server Actions za mutacije.
- Poboljšajte percipirane performanse streamanjem sporih sekcija iza Suspense granica umjesto blokiranja cijele stranice.
- Caching tretirajte kao produktnu odluku:
no-storeza kritičnu svježinu, revalidacija za sadržaj koji tolerira blagu zastarjelost.
# Zaključak#
React Server Components mijenjaju default: vaša React aplikacija može biti server-first, sigurna po defaultu i značajno “lakša” po pitanju client JavaScripta, a i dalje podržavati interaktivan UX kroz male client otoke. U Next.js App Routeru danas dobivate produkcijski spreman RSC workflow tako da stranice i podatke držite na serveru, a u "use client" ulazite samo ondje gdje se to isplati.
Ako želite pomoć oko dizajna RSC arhitekture, performance budžeta i plana migracije (App Router, Server Actions, streaming, analitika), javite se Samioda: web & mobile development.
FAQ
Više iz kategorije Web razvoj
Sve →Tehnički SEO za developere (Next.js): Sve što trebate znati u 2026.
Praktičan vodič za tehnički SEO u Next.js-u fokusiran na developere: meta tagovi, strukturirani podaci, Core Web Vitals, sitemapovi, robots.txt, kanonski URL-ovi i primjeri spremni za produkciju.
Vodič za API integracije: najbolje prakse za 2026.
Praktičan vodič za API integracije za 2026.: REST vs GraphQL, autentikacija, obrada grešaka, ponovni pokušaji, rate limiting i primjeri Next.js API ruta spremni za produkciju.
Najbolji headless CMS u 2026: Sanity vs Strapi vs Contentful (i još 2)
Praktična usporedba u 2026. top 5 headless CMS opcija—Sanity, Strapi, Contentful, Directus i Storyblok—s fokusom na developer experience, Next.js integraciju, značajke i cijene.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Progresivne web aplikacije (PWA): Potpuni vodič za 2026.
Praktičan vodič za progressive web app (PWA) u 2026.: koncepti, poslovne prednosti u odnosu na nativne aplikacije i implementacija korak‑po‑korak u Next.js-u s manifestom i primjerima koda za service worker.
Zašto Je Next.js Najbolji Framework za SEO u 2026
Saznajte zašto Next.js dominira SEO performansama u 2026. Server-side rendering, Core Web Vitals, strukturirani podaci i stvarne usporedbe performansi.
Vodič za API integracije: najbolje prakse za 2026.
Praktičan vodič za API integracije za 2026.: REST vs GraphQL, autentikacija, obrada grešaka, ponovni pokušaji, rate limiting i primjeri Next.js API ruta spremni za produkciju.