React Server Components: En praktisk guide
Server Components förändrar hur vi bygger React-appar. Så fungerar de, när du ska använda dem, och hur du undviker de vanligaste fallgroparna.
React Server Components (RSC) förändrar fundamentalt hur vi tänker kring React-applikationer. Istället för att skicka JavaScript till webbläsaren för varje komponent, renderas Server Components på servern och skickar färdig HTML. Resultatet: mindre JavaScript, snabbare sidladdning och direkt åtkomst till serverns resurser.
Men övergången från "allt renderas i webbläsaren" till "vissa saker renderas på servern" kräver ett nytt mentalt ramverk. Den här guiden ger dig det.
Server Components vs Client Components
I Next.js App Router är alla komponenter Server Components som standard. Du behöver bara markera de komponenter som behöver interaktivitet, state eller browser-API:er med "use client".
// Server Component (standard i App Router)
// Kan: hämta data direkt, läsa filer, använda databas
// Kan inte: useState, useEffect, onClick, browser-API:er
// app/products/page.tsx — Server Component
import { db } from "@/lib/database";
export default async function ProductsPage() {
// Direkt databasåtkomst — ingen API-route behövs
const products = await db.products.findMany({
orderBy: { createdAt: "desc" },
take: 20,
});
return (
<div>
<h1>Produkter</h1>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
{/* Interaktiv komponent */}
<AddToCartButton />
</div>
);
}
// ProductCard är också en Server Component
function ProductCard({ product }) {
return (
<div>
<h2>{product.name}</h2>
<p>{product.price} kr</p>
</div>
);
}// Client Component — markeras explicit
"use client";
import { useState } from "react";
export function AddToCartButton() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Lägg i varukorg ({count})
</button>
);
}
// Tumregel: "use client" ska vara så långt ner
// i komponentträdet som möjligt.
// Bara den knapp som behöver state ska vara client.Data-hämtning utan useEffect
En av de största fördelarna med Server Components är att data-hämtning sker med vanliga async/await — direkt i komponenten. Inga useEffect-loopar, inga loading states att hantera manuellt, inga race conditions.
// Traditionellt (Client Component)
"use client";
import { useState, useEffect } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch("/api/users/" + userId)
.then(res => res.json())
.then(data => setUser(data))
.catch(err => setError(err))
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <p>Laddar...</p>;
if (error) return <p>Fel: {error.message}</p>;
return <h1>{user.name}</h1>;
}
// Med Server Components — mycket enklare
import { db } from "@/lib/database";
async function UserProfile({ userId }: { userId: string }) {
const user = await db.users.findUnique({
where: { id: userId },
});
if (!user) return <p>Användaren hittades inte</p>;
return <h1>{user.name}</h1>;
}
// Loading-state? Använd loading.tsx:
// app/users/[id]/loading.tsx
export default function Loading() {
return <p>Laddar profil...</p>;
}Streaming med Suspense
Server Components stödjer streaming — du behöver inte vänta på att all data ska vara klar innan sidan börjar visas. Wrap långsamma delar i Suspenseså streamar React in dem när de är redo.
// Sidan renderas direkt med fallback
// för den långsamma delen
import { Suspense } from "react";
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Renderas direkt */}
<QuickStats />
{/* Streamar in när datan är klar */}
<Suspense fallback={<p>Laddar diagram...</p>}>
<RevenueChart />
</Suspense>
<Suspense fallback={<p>Laddar aktivitet...</p>}>
<RecentActivity />
</Suspense>
</div>
);
}
// Denna komponent tar 3 sekunder att hämta data
// men blockerar inte resten av sidan
async function RevenueChart() {
const data = await fetchRevenueData(); // 3s
return <Chart data={data} />;
}
async function RecentActivity() {
const events = await fetchActivityLog(); // 1s
return (
<ul>
{events.map(e => <li key={e.id}>{e.title}</li>)}
</ul>
);
}Server Actions — formulär utan API-routes
Server Actions ersätter behovet av API-routes för formulärhantering. Definiera en funktion med "use server" och använd den direkt i ett formulärsaction-attribut. Data skickas automatiskt till servern.
// Server Action — direkt i filen
async function createProduct(formData: FormData) {
"use server";
const name = formData.get("name") as string;
const price = Number(formData.get("price"));
// Validering
if (!name || price <= 0) {
throw new Error("Ogiltig data");
}
// Direkt databasåtkomst
await db.products.create({
data: { name, price },
});
// Uppdatera sidan
revalidatePath("/products");
redirect("/products");
}
export default function NewProductPage() {
return (
<form action={createProduct}>
<input name="name" placeholder="Produktnamn" required />
<input name="price" type="number" step="0.01" required />
<button type="submit">Skapa produkt</button>
</form>
);
}Vanliga fallgropar
Felet: Lägga "use client" på toppnivån i ett layout. Det tvingar alla barnkomponenter att bli Client Components — och du förlorar fördelarna med Server Components helt.
Felet: Försöka använda React-context i Server Components. Context är en Client Component-feature. Lösning: skicka data som props eller använd server-side patterns som cookies och headers.
Felet: Importera stora bibliotek i Server Components som sedan inte tree-shakas korrekt. Kontrollera din bundle-storlek regelbundet.
Läs mer om modern JavaScript i vår JavaScript-guide och webbutveckling 2026.