(*Hvis du gerne vil hoppe videre og dykke direkte ind i den tekniske sammenbrud, skal du hoppe til 1.
I begyndelsen af 2010'erne revolutionerede React frontend-udviklingen med sin deklarative komponentmodel og effektive virtuelle DOM diffing. Det, der begyndte som et simpelt visningsbibliotek, blev snart rygsøjlen for store single-page-applikationer (SPA'er). Disse SPAs brugte overvejende Client-Side Rendering (CSR), hvilket betyder, at browseren ville downloade en JavaScript-pakke, udføre den og bygge brugergrænsefladen helt på klienten.
Denne klientcentrerede model var fleksibel og meget interaktiv, og den definerede "moderne" webapps i årevis.
- Længere Time-to-Interactive (TTI): Hård JavaScript bundter og klient side arbejde betød, at brugerne ventede længere, før de rent faktisk kunne interagere med siden.
-
Hydration bottlenecks: Converting server-rendered HTML into an interactive app (hydration) became a performance choke point, especially as the amount of dynamic content increased.
af - Overfyldte bundter: Applikationer leverede ofte langt mere JavaScript end nødvendigt, hvilket belastede browsere med kode for funktioner eller indhold, der kunne have været leveret mere effektivt.
- Ydeevne, der ikke skaleres: Jo større og mere kompleks appen er, jo sværere bliver det at opretholde hurtig ydeevne på tværs af alle enheder og netværksforhold.
Next.js emerged to tackle some of these pain points by introducing Server-Side Rendering (SSR), Static Site Generation (SSG), and other optimizations. These techniques improved initial load times and offloaded some of the UI rendering work to the server. But even with SSR and SSG, the fundamental issue remained: we were still over-delivering JavaScript to the browser.
Fast forward to 2025. With Next.js 15 running on React 19, a new rendering paradigm has taken center stage: React Server Components (RSC). RSCs allow developers to seamlessly blend server-rendered and client-rendered components in a single React tree. The implications are significant. Static parts of the UI can now be delivered as pure HTML with zero JavaScript overhead. In other words, no client-side hydration is needed for those sections. Data fetching logic is also simplified by running inside server components, eliminating many unnecessary API calls from the browser. The result: leaner client-side bundles, faster interactions, and an application that’s far more performant and scalable.
Denne artikel er ikke en overfladisk gennemgang af RSC. Da jeg begyndte at skrive om at bruge Next.js effektivt i 2025, blev det hurtigt klart, at React Server Components fortjente en dedikeret dybdykning.
Til sidst håber jeg, at du kommer væk med den samme klarhed og anerkendelse for RSC, som jeg gjorde gennem processen med at forske og skrive dette stykke.
Intro: From CSR to RSC — How Rendering Evolved in React
Intro: Fra CSR til RSC – hvordan rendering udviklede sig i reaktionI løbet af det sidste årti har den måde, vi opbygger React-applikationer, udviklet sig grundlæggende, og med den måde, vi tænker på rendering.
🕰 A brief history of Client-Side Rendering (CSR) in React
React gained its popularity through Client-Side Rendering (CSR)- en model, hvor browseren downloader JavaScript, udfører det og bygger hele brugergrænsefladen på klienten. Denne tilgang gav udviklerne fuld kontrol over interaktivitet og tilstand, og gjorde det nemt at opbygge dynamiske enkeltsideapplikationer (SPA'er).
Men CSR kom med bemærkelsesværdige kompromisser:
- Slower initial loads, especially on mobile or poor networks
- Dårlig SEO til indholdsdrevne sider af
- JavaScript-heavy bundles — even for pages with minimal interactivity
- A hydration step was required after HTML loaded, delaying time-to-interactive af
For a while, these limitations were just “how things were.” Then Next.js changed the game.
🚀 How Next.js brought SSR and SSG to mainstream React development
When Next.js entered the scene, it introduced server-side rendering (SSR) and static site generation (SSG) as first-class citizens for React. This marked a turning point: frontend developers could now choose how and when rendering occurred.
- af
- SSR gjorde det muligt at generere sider efter anmodning, hvilket forbedrede SEO og indlæsningshastighed for dynamisk indhold. af
- SSG tillod, at indhold blev forudbygget på tidspunktet for implementering, perfekt til blogs, dokumenter og markedsføringssteder.
- Incremental Static Regeneration (ISR) brød kløften ved at tillade statiske sider at blive opdateret efter implementering.
This flexibility helped developers strike a better balance between performance, SEO, and developer experience.
But even with SSR and SSG, there was still a lingering issue: we were still sending too much JavaScript to the browser- selv for komponenter, der ikke behøvede at være interaktive.
Fremkomsten af React Server Components (RSC) i 2025
Med frigivelsen afNext.js 15 and React 19, we’ve entered a new era: React Server Components (RSC) are now a core part of how we build apps.
I modsætning til SSR, som stadig kræver hydrering og leverer JavaScript til klienten,RSC allows you to render components on the server — without sending any JavaScript to the browser at all.
Det er et stort skifte:
- af
- Components can now access server-side data directly
- Static content doesn’t require hydration
- You can mix server and client components in a single React tree, composing your rendering strategy per component
RSC doesn’t replace SSR or SSG, it complements them, unlocking finer-grained control over performance, bundle size, and rendering behavior.
In 2025, RSC is a foundational concept that every senior React engineer needs to master.
1. Why React Server Components Were Introduced
1. Why React Server Components Were IntroducedAs React applications became more complex, the industry began to feel the weight of its success. While Client-Side Rendering (CSR), Server-Side Rendering (SSR), and Static Site Generation (SSG) offered different strategies for building performant web apps, each of them carried trade-offs that grew more apparent at scale.
🚧 Limitations of CSR, SSR, and SSG
1. afHydration overhead
Even with SSR or SSG, once HTML reaches the browser, React needs to “hydrate” the page — attach event listeners, reinitialize components, and effectively rebuild the app in memory. For large component trees, hydration can be a major bottleneck for Time-To-Interactive (TTI).
2. JavaScript bundle bloat
With CSR, every component, utility, and API call that’s part of the page must be sent to the browser — regardless of whether it’s interactive or not. SSR and SSG reduce this slightly, but most of the bundle still needs to be executed on the client. As apps grow, this leads to bloated bundles that slow down the user experience.
3. Disconnected data-fetching logic
I pre-RSC-verdenen levede data uden for de komponenter, der gengav det.getServerSideProps
ellergetStaticProps
(or call APIs in useEffect
) to fetch data, then pass it into components via props. This separation added cognitive overhead and made code harder to co-locate and reuse.
Hvilke problemer RSC er designet til at løse
React Server Components (RSC) were created to address these growing pain points with a simple but powerful idea: let components execute on the server by default, and only send JavaScript to the browser when it’s absolutely necessaryaf .
Eliminate unnecessary JavaScript
RSC allows components to be rendered server-side UdenHvis en komponent ikke kræver interaktivitet, er der ikke behov for at hydrere eller indlæse sin JS-pakke overhovedet.
✅ Server-side data access within the component tree
RSC removes the artificial boundary between data fetching and rendering. Server components can use async/await
to directly access databases, file systems, or APIs — co-locating data and view logic naturally, with no need for API routes or prop drilling.
✅ Improve rendering efficiency and developer experience
Ved at flytte ikke-interaktiv logik til serveren kan udviklere opbygge lettere apps med mindre bundter og bedre ydeevne. RSC forenkler også den mentale model - du skriver kun komponenter, og lad rammen håndtere, hvor de kører og hvordan de leveres.
RSC doesn’t aim to replace SSR or SSG, instead, it complements them. It lets you think at the component level, not just the page level, about what should run on the server and what belongs in the browser.
In short: React Server Components were designed to bring modern frontend development back to its lean, fast, and maintainable roots without compromising interactivity.
2. Rendering Strategies in Next.js 15: RSC vs SSR vs CSR
2. Rendering Strategies in Next.js 15: RSC vs SSR vs CSRNext.js 15 offers developers a granular rendering model that goes far beyond traditional page-level strategies. With React Server Components (RSC)For at blive et førsteklasses koncept er det vigtigt at forstå, hvordan de sammenligner med to velkendte modeller:Server-Side Rendering (SSR)ogClient-Side Rendering (CSR).
While SSG (Static Site Generation) is still valuable in specific cases, it can be viewed as a caching strategy built on top of SSR. In contrast, RSC vs SSR vs CSR represent distinct runtime rendering paths, and understanding them is crucial for making performance- and architecture-aware decisions in 2025.
💡 Before We Compare: What Do We Mean by "Interactive Component"?
In the context of React and Next.js, an interactive component is any UI element that requires client-side JavaScript to respond to user input or browser events.
This includes (but is not limited to):
-
Buttons that update state on click
-
Forms with validation or controlled inputs
-
Dropdowns and modals that toggle open/closed
-
Animations triggered by scrolling or hover
-
Tabs, carousels, filters, sliders
-
Components that use
useState
,useEffect
, oruseReducer
af
If a component has event handlers, internal state, or relies on the DOM or browser APIs, it must run on the client.
Interactivity = Browser-side behavior + JS event listeners + local state.
Understanding this distinction helps clarify why RSC existsfor at undgå at sende JavaScript til dele af brugergrænsefladen, der ikke behøver at være interaktive.
Giver modeller på et kig
Feature
CSR (Client-Side Rendering)
Servere
Server
Kunde
JavaScript sent to browser
❌ None
✅ Yes
✅ Yes
❌ No
✅ Yes
Når det løber
On load in browser
Ideal use case
Static or data-bound views
Personligt eller dynamisk UI
Feature
CSR (Client-Side Rendering)
Feature
Feature
RSC (React Server Components)
RSC (React Server Components)
SSR (server side rendering)
CSR (Client-Side Rendering)
CSR (Client-Side Rendering)
Servere
Server
Kunde
Render location
Giver placering
Servere
Servere
Servere
Server
Kunde
Client
JavaScript sent to browser
❌ None
✅ Yes
✅ Yes
JavaScript sent to browser
JavaScript sent to browser
❌ None
❌ None
✅ Yes
✅ Yes
✅ Yes
✅ Yes
❌ No
✅ Yes
Kræver hydrering
❌ No
❌ No
Ja
Ja
✅ Yes
Interaktivitet
Interactivity
Nej
Nej
Fuld af
✅ Full
Fuld af
Adgang til serverressourcer
Adgang til serverressourcer
✅ Direct
✅ Via getServerSideProps
✅ Via getServerSideProps
❌ Needs API calls
Brug for API-opkald
Når det løber
On load in browser
Når det løber
Når det løber
On-demand eller streamet
Per request
On load in browser
On load in browser
Ideel brug case
Statisk eller databundet visning
Ideal use case
Ideel brug case
Statisk eller databundet visning
Statisk eller databundet visning
Personalized or dynamic UI
Personligt eller dynamisk UI
Interaktive strømninger, lokalt UX
Interactive flows, local UX
🔍 Think in Components, Not Just Pages
In earlier versions of Next.js, rendering strategies were applied at the page level. You had getServerSideProps
, dergetStaticProps
Uanset hvad du vælger, anvendesHele siden. This made sense in a world where rendering happened all-or-nothing — either statically at build time, or dynamically on each request.
But with React Server Components (RSC) and the app/
directory introduced in Next.js 13+ and standardized in 15, rendering is no longer a top-down, one-size-fits-all decision. It becomes a per-component concern that unlocks a new mindset.
🧠 A New Way of Thinking: Declarative and Composable Rendering
This shift is more than an API change, it's a conceptual shift in how you architect your frontend.
Declarative
I stedet for at orchestrerehow and where components are rendered manually, you now simply declare what each component does and what it needs — React and Next.js take care of the rest.
You don’t manually wire up API endpoints or pass props from SSR to components. You can just write:
// Server Component
export default async function ProductInfo() {
const product = await db.getProduct(slug)
return <div>{product.name}</div>
}
This component:
- Kører på serveren af
- Sender ikke JS til kunden af
- Kræver ikke noget getServerSideProps eller API-lag af
- Is “just a component” — no extra abstraction needed
You describe the UI and its data needs declarativelyOg det er motorerne, der tæller resten.
Composable
Different parts of your UI can use different rendering strategies — on the same page, derat the same time, and with minimal overhead.
For example:
// Product page layout
<ProductInfo /> // Server Component (no JS, rendered on the server)
<AddToCartButton /> // Client Component (interactive)
<SimilarProducts /> // Static Component (SSG with revalidation)
Disse komponenter lever sammen i samme træ, men hver især:
- af
- Runs in a different environment (server, client, build)
- Uses only the data and code it needs
- Skib præcis, hvad der kræves af browseren - ikke mere, ikke mindre af
For at gøre dette mere konkret, har jeg skabt enminimal demo that showcases how different rendering strategies can coexist on a single page.
3. How React Server Components Work Under the Hood
3. How React Server Components Work Under the HoodReact Server Components (RSC) er mere end blot en ny renderingsstrategi, de ændrer grundlæggende, hvordan komponenttræer er bygget, renderet og overført.how it works behind the scenes and how it impacts the boundaries of state, interactivity, and data.
🧱 Server/Client Boundary: A Split React Tree
React applications using RSC are no longer fully rendered on the client. Instead, the component tree is split into two worlds:
- Serverkomponenter: Kør kun på serveren. Der sendes aldrig JavaScript til browseren. Kan ikke holde lokal tilstand eller vedhæfte hørere af begivenheder. Perfekt til rendering af statisk indhold og serverbaseret logik (f.eks. af
- Client Components: Must be explicitly marked with
"use client"
. These are compiled into browser-friendly JavaScript and support full interactivity, local state,useEffect
, and event handling.
At build or runtime, React constructs a tree where server and client components coexist and stitches them together during render.
📍 What "use client"
Faktisk gør
When you add "use client"
til en fil, markerer det, at hele modulet og dets eksporter somclient-onlyBag kulisserne instruerer dette Next.js build pipeline til:
- Kompilere filen (og dens afhængigheder) til en separat JavaScript bundle
- Exclude that component from being run on the server af
- Treat it like a classic React CSR component with hydration logic af
Dette direktiv fungerer som enboundary marker between the two sides of the tree. All components above it can be server-rendered; all components below it must be rendered in the browser.
💧 Streaming: Rendering in Pieces, Not All at Once
RSC embraces streaming as a native rendering strategy. Instead of waiting for the full React tree to be built before sending it to the browser, the server streams serialized fragmentstil kunden, når de er klar.
- af
- Serverkomponenter renderes og sendes så hurtigt som muligt
- Stedholdere (f.eks. via <Suspense>) udfylder midlertidigt
- Client-komponenter hydrerer gradvist, kun når de oplades af
✅ How is this possible?
RSC introduces a concept called selective hydration. When a Client Component is rendered within a Server Component tree, React inserts a placeholder (<div data-rsc-placeholder />) and defers hydration.
Once the client has loaded the corresponding JS bundle:
- React lazily indlæser den specifikke komponent af
- Finds the placeholder and stitches it into the live tree af
- Hydrates it in isolation, without re-rendering the entire page af
This design is decoupled and progressiveDin app starter hurtigt, og interaktiviteten kommer gradvist online.
<Suspense fallback={<LoadingDetails />}>
<ProductDetails /> // Server Component
</Suspense>
<AddToCartButton /> // Client Component (hydrated later)
️ Data Fetching og Code Splitting i RSC
Another key “magic” of RSC: you can fetch data directly inside components with async/await
— without relying on getServerSideProps
, useEffect
, or manual prop-passing.
// Server Component
export default async function Dashboard() {
const stats = await getStatsForUser()
return <StatsView data={stats} />
}
Hvorfor er det muligt?
- RSC components run as real server functions, not as client-compiled modules
- They can access databases, internal APIs, file systems, or anything your server runtime supports
- The result is rendered HTML (not JS) and streamed to the client
Also:
- No hydration needed, since the result is static af
- No loading UI logic in the component itself — everything resolves before it hits the browser
- No code for this component is sent to the client — unless nested inside a client boundary af
Dette reducerer boilerplade- og bundtstørrelsen betydeligt, samtidig med at logikken holdes kollokeret med brugergrænsefladen – et langvarigt React-mål, der endelig blev realiseret i skala.
🚫 State, Hooks, and Lifecycle Considerations
RSC does not supportTraditionelle hooks somuseState
, deruseEffect
elleruseRef
, because they don’t run in the browseraf .
|
|
|
|
❌ |
✅ |
af
| ✅ (hvis det er statisk) | af |
async / vente |
| (skal indpakkes i effekter) |
Event handlers |
❌ |
✅ |
useState
Brug af kontekst
Event handlers
❌
✅
Funktioner
Funktioner
Server Component
Server Component
Client Component
Brug af
Brug af
useState
useEffect
useEffect
❌
❌
✅
Brug af kontekst
Brug af kontekst
useContext
✅ (if static)
async/await
❌ (should wrap in effects)
Event handlers
❌
✅
Event handlers
Event handlers
❌
❌
✅
This enforces a clean separation of responsibilities:
- af
- Serverkomponenter: data og layout
- Client Components: interactivity and local state
React Server Components are designed to simplify your app. Once you internalize the boundary rules, the streaming model, and async data access, you can compose fast, personalized, and minimal-JS apps with far less boilerplate than before.
4. What’s the Best Practice? Combining RSC, SSR, and SSG
4. What’s the Best Practice? Combining RSC, SSR, and SSGOne of the most common questions React engineers face in Next.js 15 isn’t “should I use RSC?” — it’s “how do I combine RSC with SSR and SSG in a maintainable, high-performance way?”
The beauty of Next.js 15 is that you’re no longer limited to one rendering strategy per page. Instead, you can now compose rendering strategies at the component level, applying the most appropriate approach to each part of the UI.
This section introduces a practical framework for making that decision based on actual architectural needs.
🧭 Start with the Core Question: What does this component need?
Ask these four questions for every component:
- af
- Does it need to be interactive?
- ✅ Yes → Use a Client Component
af - Har det brug for sikre, anmodningsspecifikke eller realtidsdata? ✅ Ja → Overvej SSR af
- Can it be precomputed or infrequently updated?
- ✅ Yes → Prefer SSG
af - Does it fetch server data but never need to run on the client?
- ✅ Yes → Use RSC
Eksempel: Produktside Strategi nedbrydning
Here’s how a typical e-commerce prduct page might be composed using all three strategies:
Component | af Rendering Strategy | afÅrsagen |
---|---|---|
|
SSR | af Depends on user session, dynamic per request |
| af
SSG (with ISR) |
Safe to cache at build-time, can revalidate every 24h or per tag |
|
RSC + streaming | af
Frequently changing, streamed in with Suspense to not block TTFB |
Component
Rendering Strategy
PriceWithPersonalization
SSR
RelatedProducts
Safe to cache at build-time, can revalidate every 24h or per tag
StockStatusBanner
RSC + streaming
Frequently changing, streamed in with Suspense to not block TTFB
Component
Rendering Strategy
Component
Component
Rendering Strategy
Rendering Strategy
Reason
ProductDetails
ProductDetails
RSC
RSC
Hentet fra DB, ingen interaktivitet, ingen behov for at hydrere
PriceWithPersonalization
SSR
PriceWithPersonalization
PriceWithPersonalization
SSR
SSR
Depends on user session, dynamic per request
Depends on user session, dynamic per request
TilføjCartButton
AddToCartButton
af CSR
CSR
Requires interactivity and local state
Requires interactivity and local state
RelatedProducts
Safe to cache at build-time, can revalidate every 24h or per tag
RelatedProducts
RelatedProducts
SSG (with ISR)
SSG (with ISR)
Safe to cache at build-time, can revalidate every 24h or per tag
Sikker til cache ved opbygning, kan genvalideres hver 24. time eller pr. tag
StockStatusBanner
RSC + streaming
Frequently changing, streamed in with Suspense to not block TTFB
StockStatusBanner
StockStatusBanner
RSC + streaming
RSC + streaming
Frequently changing, streamed in with Suspense to not block TTFB
Frequently changing, streamed in with Suspense to not block TTFB
Alle komponenter gørjust what it needs to do — no more, no less. No full-page hydration, no global data fetching, no unnecessary JavaScript.
📐 Design Best Practices for Combining Strategies
✅ 1. Start Server-First
Design every component as a Server Component by default. Opt into interactivity ("use client"
) only when necessary. This keeps bundles smaller and simplifies testing.
✅ 2. Keep boundaries clear
Use folder naming or filename suffixes to make boundaries explicit:
/components
/server/ProductDetails.tsx
/client/AddToCartButton.tsx
/shared/ReviewStars.tsx
✅ 3. Embrace Suspense for progressive delivery
Brug af<Suspense>
to stream in non-critical RSCs without blocking the whole page:
<Suspense fallback={<LoadingReviews />}>
<ReviewList />
</Suspense>
✅ 4. Co-locate logic with components
Del ikke data-fetching og brugergrænseflade mellem filer, medmindre det er nødvendigt.async
logic directly inside the component tree — the framework takes care of the rest.
Brug ISR (Incremental Static Regeneration) smart
For cacheable, high-traffic pages like blog articles or marketing sections, use SSG + revalidation:
export const revalidate = 3600 // regenerate every hour
⚠️ Common Mistakes to Avoid
- ❌ Using
"use client"
by default — you’ll end up with CSR all over again af - ❌ Fetching data in client components when it could be server-fetched
- ❌ Passing too much data between RSC and client components via props — instead, let client components be focused, isolated, and stateful
- ❌ Recreating SSR-style
getServerSideProps
logic inside RSC — no need, RSC is server-side
✅ Decision Tree Summary
Here’s a simplified guide:
Is it interactive?
│
├── Yes → Client Component (CSR)
│
└── No
│
├── Needs per-request data? → SSR
│
├── Can be pre-rendered? → SSG
│
└── Otherwise → RSC
You don’t need to memorize it. Once you internalize how rendering maps to responsibility, the decisions become intuitive.
The best practice isn’t about picking “the best rendering strategy.”
It’s aboutdesigning rendering as an intentional part of your component architecture — with clarity, purpose, and performance in mind.
6. Looking Ahead: Why RSC Is More Than Just a Feature
6. Looking Ahead: Why RSC Is More Than Just a FeatureReact Server Components are not just a performance optimization or a DX enhancement — they represent a foundational shift in how we build React applications. Much like React Hooks in 2019, RSC in 2025 is redefining the baseline for frontend architecture.
🧠 RSC Changes the Mental Model of Building in React
Traditional React development was always built on this assumption:
“The browser owns the runtime. We hydrate everything. Every piece of logic and data must live in the client, or be fetched via API.”
RSC breaks that assumption.
Med RSC spørger du nu:
- Can I skip hydration entirely?
- Can this component run purely on the server?
- Can I colocate backend logic with my UI? af
Det giver os tilbagethe ability to separate display logic and interactivity cleanlyIkke med træk og træk, men medfirst-class architectural boundaries.
It’s no longer “client-first.” It’s “purpose-first.”
Each part of your UI exists where it’s most efficient — server, client, or static.
🌐 Ecosystem Shift Toward Server-First Rendering
RSC isn’t happening in isolation. The broader frontend ecosystem is undergoing a server-first rendering renaissance.
Frameworks like:
- Remix læner stærkt ind i serverdataindlæsning og formularhandlinger.
- Astro embraces zero-JS by default, shipping only islands of interactivity. af
- Qwik tager hydrering til det yderste - udskyder alle JS indtil det udtrykkeligt er nødvendigt.
- Next.js 15, with RSC and App Router, now puts per-component rendering at the center of the developer experience. af
This isn’t a coincidence. It’s a reflection of a hard truth we’ve all felt:
Sending less JavaScript is the only way to scale interactivity and performance on the modern web.
React Server Components are the React-native answer to that challenge — deeply integrated, ergonomic, and production-ready.
🔮 What to Expect Next
The evolution is still ongoing. As React 19 and the ecosystem mature, we can expect:
- More granular debugging and profiling tools for RSC trees af
- Better DevTools integration to show boundaries and hydration timelines
- Higher-order patterns to abstract rendering strategy (e.g.,
<ServerOnly>
,<DeferredClient>
wrappers) af - Broader adoption in design systems, frameworks, and libraries (e.g., RSC-aware UI kits) af
💬 Enjoyed the read?
If this article helped you think differently about React and Next.js
Follow me on Hackerne for more deep dives
HackerneOr connect with me on LinkedInat chatte om React, arkitektur eller RSC migration
LinkedIn