Server Components في Next.js — دليل شامل 2026
دليل عملي مفصل لـ Server Components في Next.js — Server vs Client Components، use client، والتركيب
في المقال السابق، تعلمت App Router. الآن سنتعمق في Server Components — وهي أكبر ميزة في Next.js 15، وتغيّر طريقة تفكيرك في بناء التطبيقات.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم Server Components، مع تمارين وحلول.
المشكلة التي تحلها Server Components
قبل Server Components:
// كل شيء في المتصفح
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch("/api/user")
.then((res) => res.json())
.then(setUser);
}, []);
if (!user) return <div>جاري التحميل...</div>;
return <h1>{user.name}</h1>;
}
المشاكل:
- ❌ الكود يُرسل للمتصفح.
- ❌ 3 مراحل (تحميل، fetch، عرض).
- ❌ بيانات لا تظهر لـ SEO.
- ❌ Bundle كبير.
بعد Server Components:
// يعمل على السيرفر
async function UserProfile() {
const user = await getUser(); // مباشرة من DB
return <h1>{user.name}</h1>;
}
الحلول:
- ✅ الكود لا يُرسل للمتصفح.
- ✅ مرحلة واحدة.
- ✅ بيانات لـ SEO.
- ✅ Bundle أصغر.
الفرق الأساسي
| المعيار | Server Component | Client Component |
|---|---|---|
| الافتراضي | ✅ نعم | ❌ يحتاج "use client" |
| useState | ❌ | ✅ |
| useEffect | ❌ | ✅ |
| onClick | ❌ | ✅ |
| async/await | ✅ مباشرة | ❌ (في useEffect) |
| الوصول لقاعدة البيانات | ✅ | ❌ |
| الوصول للملفات | ✅ | ❌ |
| البيانات الحساسة | ✅ آمن | ❌ مكشوف |
| Bundle Size | ✅ صفر | ⚠️ يزيد |
Server Components
افتراضياً، كل مكون في app/ هو Server Component.
1. جلب بيانات من API
// app/users/page.tsx
async function getUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users", {
cache: "no-store", // ← لا تخزين
});
return res.json();
}
export default async function UsersPage() {
const users = await getUsers();
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
⚠️ لاحظ:
asyncمباشرة.awaitفي المكوّن.- لا
useStateولاuseEffect.
2. الوصول لقاعدة البيانات
// app/posts/page.tsx
import { prisma } from "@/lib/prisma";
export default async function PostsPage() {
const posts = await prisma.post.findMany();
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
الفائدة: لا تحتاج API وسيط — مباشرة من DB.
3. الوصول للملفات
// app/data/page.tsx
import fs from "fs/promises";
export default async function DataPage() {
const content = await fs.readFile("./data.json", "utf-8");
const data = JSON.parse(content);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Client Components
للاستخدام Client Component، أضف "use client" في أعلى الملف.
1. استخدام useState
// app/counter/page.tsx
"use client";
import { useState } from "react";
export default function CounterPage() {
const [count, setCount] = useState(0);
return (
<div>
<p>العدد: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
2. استخدام useEffect
"use client";
import { useState, useEffect } from "react";
export default function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer);
}, []);
return <p>{time.toLocaleTimeString("ar-EG")}</p>;
}
3. استخدام onMouseOver
"use client";
export default function HoverCard() {
return (
<div
onMouseOver={() => console.log("Hover!")}
className="p-4 bg-blue-100"
>
مرر الفأرة
</div>
);
}
متى تستخدم كل نوع؟
| الحالة | النوع |
|---|---|
| عرض بيانات ثابتة | Server |
| جلب من API/DB | Server |
| قراءة ملفات | Server |
| بيانات حساسة (API Keys) | Server |
| useState / useEffect | Client |
| onClick / onChange | Client |
| Browser APIs | Client |
| Context / Providers | Client |
تركيب المكونات
1. Server داخل Server
// app/dashboard/page.tsx
import Stats from "./Stats";
import RecentOrders from "./RecentOrders";
export default async function Dashboard() {
return (
<div>
<Stats />
<RecentOrders />
</div>
);
}
✅ يعمل — Server داخل Server.
2. Client داخل Server
// app/page.tsx
import LikeButton from "./LikeButton"; // ← "use client"
export default function Home() {
return (
<div>
<h1>مقالي</h1>
<LikeButton />
</div>
);
}
✅ يعمل — Client داخل Server.
3. Server داخل Client (مشكلة!)
// ❌ خطأ:
"use client";
import ServerComponent from "./ServerComponent";
export default function ClientWrapper() {
return <ServerComponent />; // ← لن يعمل
}
❌ لا يعمل — Server داخل Client.
4. الحل: تمرير Server كـ children
// ✅ صحيح:
"use client";
export default function ClientWrapper({
children,
}: {
children: React.ReactNode;
}) {
return <div>{children}</div>;
}
// app/page.tsx (Server)
import ClientWrapper from "./ClientWrapper";
import ServerComponent from "./ServerComponent";
export default function Page() {
return (
<ClientWrapper>
<ServerComponent />
</ClientWrapper>
);
}
✅ يعمل! — Server يُمرَّر كـ children لـ Client.
مثال عملي: صفحة منتج
1. Server Component (جلب البيانات)
app/products/[id]/page.tsx:
import AddToCartButton from "./AddToCartButton";
async function getProduct(id: string) {
const res = await fetch(`https://api.example.com/products/${id}`);
return res.json();
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
return (
<div className="max-w-4xl mx-auto p-8">
<img src={product.image} alt={product.name} className="w-full h-96 object-cover rounded-xl" />
<h1 className="text-3xl font-bold mt-6">{product.name}</h1>
<p className="text-gray-600 mt-4">{product.description}</p>
<p className="text-2xl font-bold text-blue-600 mt-4">
{product.price} $
</p>
<AddToCartButton productId={product.id} />
</div>
);
}
2. Client Component (زر الإضافة)
app/products/[id]/AddToCartButton.tsx:
"use client";
import { useState } from "react";
export default function AddToCartButton({
productId,
}: {
productId: string;
}) {
const [added, setAdded] = useState(false);
const handleClick = () => {
// ... منطق إضافة للسلة
setAdded(true);
setTimeout(() => setAdded(false), 2000);
};
return (
<button
onClick={handleClick}
disabled={added}
className="mt-6 bg-blue-600 text-white px-8 py-3 rounded-lg font-bold hover:bg-blue-700 disabled:bg-green-600 transition"
>
{added ? "✅ تمت الإضافة" : "🛒 أضف للسلة"}
</button>
);
}
الفائدة: الصفحة Server (سريعة، SEO)، والزر Client (تفاعلي).
Streaming و Suspense
Suspense تسمح بتحميل أجزاء الصفحة بالتدريج.
import { Suspense } from "react";
export default function Dashboard() {
return (
<div>
<h1>لوحة التحكم</h1>
<Suspense fallback={<p>جاري تحميل الإحصائيات...</p>}>
<Stats />
</Suspense>
<Suspense fallback={<p>جاري تحميل الطلبات...</p>}>
<RecentOrders />
</Suspense>
</div>
);
}
الفائدة: الصفحة تبدأ بالظهور، والأجزاء البطيئة تُحمّل لاحقاً.
cache في fetch
// تخزين دائم (Static)
fetch(url, { cache: "force-cache" }); // افتراضي
// لا تخزين (Dynamic)
fetch(url, { cache: "no-store" });
// إعادة التحقق كل 60 ثانية (ISR)
fetch(url, { next: { revalidate: 60 } });
| الإعداد | السلوك |
|---|---|
force-cache | تخزين دائم |
no-store | لا تخزين (دائماً جديد) |
revalidate: 60 | إعادة التحقق كل 60 ثانية |
تمارين عملية
تمرين 1: Server Component
أنشئ صفحة تعرض قائمة مستخدمين.
الحل:
async function getUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
return res.json();
}
export default async function UsersPage() {
const users = await getUsers();
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
تمرين 2: Client Component
أنشئ زر تبديل الوضع الداكن.
الحل:
"use client";
import { useState } from "react";
export default function ThemeToggle() {
const [dark, setDark] = useState(false);
return (
<button onClick={() => setDark(!dark)}>
{dark ? "☀️" : "🌙"}
</button>
);
}
تمرين 3: مزج Server + Client
أنشئ صفحة مقالات مع زر "Like".
الحل:
// Server
async function getPosts() {
// ...
}
export default async function Posts() {
const posts = await getPosts();
return (
<ul>
{posts.map((p) => (
<li key={p.id}>
{p.title}
<LikeButton postId={p.id} />
</li>
))}
</ul>
);
}
تمرين 4: Suspense
أضف Suspense لصفحة.
الحل:
import { Suspense } from "react";
<Suspense fallback={<p>جاري التحميل...</p>}>
<SlowComponent />
</Suspense>
تمرين 5: cache
جرّب cache: "no-store".
الحل:
const res = await fetch(url, { cache: "no-store" });
تمرين 6: Server مع DB
اجلب بيانات من ملف.
الحل:
import fs from "fs/promises";
export default async function DataPage() {
const content = await fs.readFile("./data.json", "utf-8");
return <pre>{content}</pre>;
}
تمرين 7: Client مع useEffect
أنشئ ساعة رقمية.
الحل:
"use client";
import { useState, useEffect } from "react";
export default function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer);
}, []);
return <p>{time.toLocaleTimeString("ar-EG")}</p>;
}
تمرين 8: تطبيق مختلط
ابنِ تطبيق يستخدم Server و Client معاً.
الحل: (راجع مثال صفحة المنتج)
حل المشاكل الشائعة
🔴 المشكلة 1: useState is not defined
السبب: استخدمت useState في Server Component.
الحل: أضف "use client".
🔴 المشكلة 2: async/await في Client
السبب: Client Component لا يمكن أن يكون async.
الحل: استخدم useEffect لجلب البيانات.
🔴 المشكلة 3: Server داخل Client
السبب: حاولت استيراد Server Component في Client Component.
الحل: مرّره كـ children.
🔴 المشكلة 4: window is not defined
السبب: استخدمت window في Server.
الحل: استخدم "use client" أو typeof window !== "undefined".
🔴 المشكلة 5: البيانات لا تُحدّث
السبب: cache يخزّن البيانات.
الحل:
fetch(url, { cache: "no-store" });
جدول مقارنة سريعة
| الميزة | Server | Client |
|---|---|---|
| async/await | ✅ | ❌ |
| useState | ❌ | ✅ |
| useEffect | ❌ | ✅ |
| onClick | ❌ | ✅ |
| DB Access | ✅ | ❌ |
| FS Access | ✅ | ❌ |
| Bundle Size | 0 KB | يزيد |
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| فهم Server vs Client | ⬜ |
| Server Component مع fetch | ⬜ |
| Client Component مع useState | ⬜ |
| مزج Server + Client | ⬜ |
| Suspense | ⬜ |
| cache في fetch | ⬜ |
| حل التمارين الثمانية | ⬜ |
ماذا بعد هذا المقال؟
الآن بعد أن أتقنت Server Components، أنت جاهز للمقال التالي:
- Data Fetching — جلب البيانات المتقدم.
- API Routes — بناء APIs.
- Authentication — المصادقة.
الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هي Server Components.
- ✅ Server vs Client Components.
- ✅
"use client". - ✅ مزج Server + Client.
- ✅ Suspense و Streaming.
- ✅ cache في fetch.
- ✅ حل 8 تمارين عملية.
تذكر: Server Components هي الميزة الأكبر في Next.js 15 — استخدمها بحكمة.
App Router في Next.js — دليل شامل 2026
جلب البيانات في Next.js — دليل شامل 2026
📚 مقالات ذات صلة
مشروع Next.js متكامل — مدونة احترافية 2026
مشروع عملي شامل لبناء مدونة كاملة بـ Next.js — مع Markdown، SEO، نشر تلقائي، وتصميم احترافي
نشر تطبيق Next.js — دليل شامل 2026
دليل عملي مفصل لنشر تطبيق Next.js — Vercel، Firebase، Netlify، والتصدير الثابت
المصادقة في Next.js — دليل شامل 2026
دليل عملي مفصل للمصادقة في Next.js — NextAuth.js، JWT، Middleware، وحماية المسارات