Next.js📅 2026-11-14⏱ 9 دقائق قراءة⚡ مقال 4 من 10

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 ComponentClient 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/DBServer
قراءة ملفاتServer
بيانات حساسة (API Keys)Server
useState / useEffectClient
onClick / onChangeClient
Browser APIsClient
Context / ProvidersClient

تركيب المكونات

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" });

جدول مقارنة سريعة

الميزةServerClient
async/await✅❌
useState❌✅
useEffect❌✅
onClick❌✅
DB Access✅❌
FS Access✅❌
Bundle Size0 KBيزيد

قائمة تحقق نهائية

المهمةالحالة
فهم Server vs Client⬜
Server Component مع fetch⬜
Client Component مع useState⬜
مزج Server + Client⬜
Suspense⬜
cache في fetch⬜
حل التمارين الثمانية⬜

ماذا بعد هذا المقال؟

الآن بعد أن أتقنت Server Components، أنت جاهز للمقال التالي:

  1. Data Fetching — جلب البيانات المتقدم.
  2. API Routes — بناء APIs.
  3. Authentication — المصادقة.

الخلاصة

في هذا المقال، تعلمت:

  • ✅ ما هي Server Components.
  • ✅ Server vs Client Components.
  • ✅ "use client".
  • ✅ مزج Server + Client.
  • ✅ Suspense و Streaming.
  • ✅ cache في fetch.
  • ✅ حل 8 تمارين عملية.

تذكر: Server Components هي الميزة الأكبر في Next.js 15 — استخدمها بحكمة.

📚 مقالات ذات صلة

Next.js

مشروع Next.js متكامل — مدونة احترافية 2026

مشروع عملي شامل لبناء مدونة كاملة بـ Next.js — مع Markdown، SEO، نشر تلقائي، وتصميم احترافي

Next.js

نشر تطبيق Next.js — دليل شامل 2026

دليل عملي مفصل لنشر تطبيق Next.js — Vercel، Firebase، Netlify، والتصدير الثابت

Next.js

المصادقة في Next.js — دليل شامل 2026

دليل عملي مفصل للمصادقة في Next.js — NextAuth.js، JWT، Middleware، وحماية المسارات