Khaled Ahmed
الرئيسية المدوّنة الأداء وتحسين محركات البحث
الأداء وتحسين محركات البحث

تحسين أداء Next.js: كيف تحقق نتيجة 100 كاملة في Lighthouse في 2026

Khaled Ahmed 9 min read

تحسين أداء Next.js في 2026 ما بقاش رفاهية مخصصة لفِرَق الهندسة اللي بتلاحق أرقام للعرض على Twitter. النهارده، الوصول لـ 100 كامل في Lighthouse بيترجم نفسه مباشرة لترتيب أعلى في Google، ومعدل ارتداد أقل، وزيادة محسوسة في معدلات التحويل. أنا خالد أحمد، مطور Full Stack أول مقيم في القاهرة، عندي أكتر من 5 سنين خبرة وأكتر من 25 مشروع شُحن للإنتاج بين مصر والسعودية والإمارات والمملكة المتحدة وسويسرا وفرنسا وألمانيا والكويت. خلال آخر تلات سنين راجعت عشرات من قواعد كود Next.js — من MVPs لشركات ناشئة لحد لوحات تحكم enterprise بتخدم ملايين الطلبات — وأقدر أقولك إن الفرق بين سكور 62 و 100 نادراً ما يكون الـ framework نفسه. الفرق هو عادات المطور اللي اتراكمت فوقه.

الدليل ده هو دفتر التكتيكات الفعلي اللي بستخدمه لما عميل بيسلملي تطبيق Next.js بطيء ويطلب مني أصلحه. مفيش حشو، مفيش spam مدوّر من مدونات تانية، ومفيش كلام من نوع "فعّل caching وادعي ربنا". هنمشي على كل تقنية بطبقها، بالترتيب اللي بطبقها فيه، بكود حقيقي، أرقام حقيقية، والمقايضات (trade-offs) اللي محدش بيتكلم عنها على YouTube. في النهاية هيكون عندك checklist كاملة لتحسين أداء Next.js تقدر تشتغل بيها على مشروعك الليلة.

الخلاصة السريعة — إزاي توصل لسكور Lighthouse كامل 100 في Next.js 15+:
  1. اعتبر React Server Components هي الافتراضي وقلل حدود "use client" لأقصى درجة.
  2. استخدم next/image مع صيغة AVIF وخاصية priority لصورة الـ LCP.
  3. استضِف الخطوط على سيرفرك بـ next/font عشان تشيل الطلبات اللي بتحجب الرندر.
  4. استخدم ISR أو Partial Prerendering بدل SSR الكامل.
  5. أجّل سكربتات الـ third-party بـ next/script strategy="lazyOnload".
  6. فعّل Turbopack وتقسيم الكود على مستوى الـ route بـ next/dynamic.
  7. راقب Core Web Vitals (LCP تحت 2.5 ثانية، INP تحت 200ms، CLS تحت 0.1) بـ Vercel Speed Insights.

سكور Lighthouse 100 بيقيس إيه فعلاً في 2026

قبل ما تحسّن أي حاجة، لازم تفهم انت بتحسّن لإيه بالظبط. Lighthouse 12 (النسخة اللي بتشحن مع Chrome 132+ في 2026) بيشغّل أربع تدقيقات: Performance و Accessibility و Best Practices و SEO. كل واحد فيهم بياخد سكور من 100، وأغلب المطورين بيتعلقوا برقم الـ Performance ويهملوا التلاتة التانيين. ده غلط، لأن إشارة Page Experience عند Google بتوزّن الأربعة كلهم.

سكور الـ Performance هو متوسط مرجّح لخمس مقاييس معملية. في Lighthouse 12 الأوزان كالتالي: Largest Contentful Paint (25%)، Total Blocking Time (30%)، Cumulative Layout Shift (25%)، First Contentful Paint (10%)، و Speed Index (10%). لاحظ إن TBT — اللي هو وكيل معملي لـ Interaction to Next Paint — بقى المسيطر. ده مقصود. Google استبدلت First Input Delay بـ INP في مارس 2024 لأن FID كان بيقيس أول تفاعل بس. INP بيقيس أسوأ تفاعل عبر الجلسة كلها، اللي هو اللي المستخدمين بيحسوا بيه فعلاً.

الـ Accessibility بيفحص تباين الألوان، استخدام ARIA، HTML الدلالي، والتنقل بالكيبورد. الـ Best Practices بيمسك مشاكل HTTPS، الـ APIs المهجورة، أخطاء الـ console، ونسب أبعاد الصور. الـ SEO بيفحص الـ meta tags، قابلية الزحف، البيانات المنظمة، وحجم أهداف اللمس. الوصول لـ 100 في الأربعة ممكن لكنه مش تلقائي — حتى scaffold افتراضي من create-next-app بيجيب حوالي 92-95 في تدقيق mobile بارد من غير تدخل.

ليه Core Web Vitals (LCP و INP و CLS) استبدلت FID كإشارة ترتيب عند Google

Core Web Vitals من Google هي التلات مقاييس في العالم الحقيقي اللي بتغذّي إشارة ترتيب Page Experience. بتتسحب من Chrome User Experience Report (CrUX)، اللي بيجمّع بيانات مجهولة الهوية من مستخدمين Chrome حقيقيين حوالين العالم. ده مهم: سكورك المعملي في Lighthouse هو لقطة تخليقية، لكن CrUX هو اللي Google بترتب عليه فعلاً. ممكن تجيب 100 في Lighthouse وبرضو يتم معاقبتك لو مستخدمينك الحقيقيين على أجهزة Android متوسطة بيشوفوا LCP بأربع ثواني.

عتبات 2026 لتقييم "Good" مفيش فيها تغيير من 2024: LCP أقل من 2.5 ثانية، INP أقل من 200 ميلي ثانية، CLS أقل من 0.1. محتاج 75% من تحميلات صفحاتك (عند الـ percentile الـ 75) توصل لـ "Good" في التلاتة. تفوّت واحد و Google بتنزّلك في ترتيب الموبايل — عقوبة بتتراكم على شهور لما بيانات CrUX بتاعتك تشيخ.

التحول من FID لـ INP كان وحشي على تطبيقات الصفحة الواحدة. FID كان سهل: كان بيحسب التأخير قبل أول event handler يشتغل بس. INP بيقيس الزمن الكامل لكل كليك ولمسة وضغطة كيبورد — من الإدخال للرسمة اللي بعديها. الـ hydration التقيلة، بندلات client كبيرة، و event handlers متزامنة كل ده بيفجّر INP. عشان كده تحسين INP في Next.js بقى أكبر رافعة أداء لمعظم تطبيقات React اللي بدققها.

تدقيق تطبيق Next.js بتاعك: Lighthouse CLI ولا PageSpeed Insights ولا WebPageTest

شغّل التلاتة. أنا جاد. كل أداة بتمسك حاجات التانيين بيفوتوها. ده الـ kickoff الافتراضي بتاعي للتدقيق:

# 1. Lighthouse CLI for reproducible local runs
npx lighthouse https://yoursite.com \
  --preset=desktop \
  --output=html \
  --output-path=./reports/desktop.html \
  --chrome-flags="--headless"

npx lighthouse https://yoursite.com \
  --preset=mobile \
  --throttling.cpuSlowdownMultiplier=4 \
  --output=html \
  --output-path=./reports/mobile.html

# 2. PageSpeed Insights API for CrUX field data
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://yoursite.com&strategy=mobile&key=$PSI_KEY" \
  | jq '.loadingExperience.metrics'

# 3. WebPageTest for waterfall + filmstrip
# Use the web UI at webpagetest.org with "Mobile - Moto G4" + "4G" profile

Lighthouse CLI بيديك نتائج معملية قابلة للتكرار ومع throttling. PageSpeed Insights بيظهر بيانات CrUX الميدانية عشان تشوف اللي المستخدمين بيختبروه فعلاً. WebPageTest ملوش منافس في فهم waterfall الطلبات — هتلاقي سكربتات third-party حاجبة و CSS مش مستخدم الأدوات التانية بتعدّي عليهم. لو معندكش وقت غير لواحد، ابدأ بـ PageSpeed Insights، لأن بيانات CrUX هي اللي Google بترتب عليها.

تحديد ميزانية أداء واقعية قبل ما تبدأ التحسين

أغلب الفِرَق بتتخطى الخطوة دي وبتدفع التمن بعدين. ميزانية الأداء هي عقد مكتوب بين الهندسة والمنتج: "مفيش صفحة بتشحن بأكتر من X كيلوبايت JavaScript، و Y كيلوبايت صور، و TTFB أعلى من Z ميلي ثانية." من غير ميزانية، كل ميزة جديدة بترفع البندل 30 كيلوبايت لحد ما الصفحة الرئيسية بتاعتك بتوزن 2 ميجابايت ومحدش عارف ده حصل إمتى.

ميزانيتي الافتراضية لمواقع المحتوى هي: 170 كيلوبايت JavaScript (مضغوط gzip، لكل route)، 100 كيلوبايت CSS، 800 كيلوبايت وزن صفحة كامل في أول تحميل، TTFB تحت 600 ميلي من cache بارد، و LCP تحت 2.0 ثانية على موبايل متوسط. للوحات التحكم بسمح بـ 300 كيلوبايت JS بس بحافظ على TTFB صارم. اكتب الميزانية في الـ CI: @next/bundle-analyzer زائد سكربت مخصص يفشّل الـ build لو أي route تعدّى نصيبه. ده نفس المنهج اللي شرحته في تحليلي لـ ليه موقعك بيحمّل ببطء وإزاي تصلحه — الوقاية بتغلب العلاج في كل مرة.

تقنية 1: اعتبار React Server Components افتراضي وتقليص بندل الكلاينت

دي أعلى رافعة تأثير في مشاريع App Router. React Server Components بترندر بالكامل على السيرفر وبتشحن صفر JavaScript للكلاينت لمنطقها هي. الـ JS الوحيد اللي بيتشحن هو للمكونات اللي معلّمة صراحة بـ "use client". في موقع تسويقي عادي، تقدر تخلي 80-90% من المكونات على السيرفر وتشحن بندل كلاينت أقل من 80 كيلوبايت مضغوط.

الغلطة اللي بشوفها على طول: مطور بيرمي "use client" فوق layout.tsx أو page.tsx لأن طفل صغير محتاج useState. حدود الكلاينت دي بتجر كل الذرّية في بندل الكلاينت. النمط الصحيح هو إنك تنزّل "use client" لأعمق نقطة في الشجرة — مثالياً على مكونات الـ leaf.

// BAD: marks the entire page as client
"use client";
import { useState } from "react";
import HeavyMarkdownRenderer from "./HeavyMarkdown";

export default function ArticlePage({ article }) {
  const [liked, setLiked] = useState(false);
  return (
    <article>
      <HeavyMarkdownRenderer content={article.body} />
      <button onClick={() => setLiked(!liked)}>
        {liked ? "Liked" : "Like"}
      </button>
    </article>
  );
}

// GOOD: page stays server, only the button is client
// page.tsx (server component)
import HeavyMarkdownRenderer from "./HeavyMarkdown";
import LikeButton from "./LikeButton";

export default function ArticlePage({ article }) {
  return (
    <article>
      <HeavyMarkdownRenderer content={article.body} />
      <LikeButton articleId={article.id} />
    </article>
  );
}

// LikeButton.tsx
"use client";
import { useState } from "react";
export default function LikeButton({ articleId }) {
  const [liked, setLiked] = useState(false);
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? "Liked" : "Like"}
    </button>
  );
}

إعادة الهيكلة دي لوحدها — على مشروع عميل حقيقي لشركة إعلام سويسرية — نزّلت JS الـ route من 312 كيلوبايت لـ 71 كيلوبايت. LCP اتحسن بـ 1.4 ثانية عند الـ percentile الـ 75 من مستخدمين الموبايل. أداء React Server Components حقيقي، لكن بشرط واحد إنك تحترم الحدود.

تقنية 2: إتقان next/image لـ LCP مثالي

الصور هي عنصر الـ LCP في حوالي 70% من الصفحات اللي بدققها. خلي next/image صح وهتجيب LCP ببلاش. خليه غلط ومفيش تحسين سيرفر هينقذك.

أربع قواعد ما بكسرهاش أبداً لتحسين next/image:

  • ضيف priority لصورة الـ LCP وبس. ده بيعمل preload لها وبيوقف الـ lazy loading. لو ضفت priority لكل حاجة، يبقى زي ما تكون مضفهاش لحاجة.
  • دايماً وفّر sizes دقيقة. الافتراضي "100vw" بيهدر bandwidth على الديسكتوب. استخدم sizes="(max-width: 768px) 100vw, 50vw" أو حاجة شبيهة حسب layout الـ CSS بتاعك.
  • فعّل AVIF في next.config.js. AVIF أصغر بـ 30-50% من WebP. المتصفحات اللي ما بتدعمهوش بترجع لـ fallback تلقائياً.
  • استخدم blur placeholder للصور فوق الـ fold. بيمنع الفلاش الواضح وبيحسّن الأداء المدرك.
// next.config.js
module.exports = {
  images: {
    formats: ["image/avif", "image/webp"],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 31536000, // 1 year
  },
};

// Hero image (LCP element)
import Image from "next/image";
import hero from "./hero.jpg";

<Image
  src={hero}
  alt="Senior developer at desk"
  priority
  placeholder="blur"
  sizes="(max-width: 768px) 100vw, 1200px"
  className="w-full h-auto"
/>

حيلة دقيقة لتحسين LCP في Next.js: لو صورة LCP بتاعتك بتتحمل من CMS (يعني مش قادر تستوردها كملف ثابت)، استخدم fetchPriority="high" بالإضافة لـ priority، واعمل preconnect لدومين الـ CMS في layout.tsx بـ <link rel="preconnect">. ده بيوفر 100-300 ميلي ثانية من الـ LCP على الشبكات البطيئة.

تقنية 3: استضافة الخطوط ذاتياً بـ next/font/google و next/font/local

طلبات الخطوط الخارجية لـ fonts.googleapis.com بتحجب الرندر افتراضياً. بتضيف DNS lookup، و TLS handshake، وتحميل CSS، وبعد كل ده ملفات الخطوط الفعلية. على اتصال 4G ده بسهولة 500-800 ميلي ثانية وقت ضايع قبل ما النص بتاعك يترسم.

باكدج next/font بيحمّل ملفات الخطوط وقت الـ build، ويستضيفها ذاتياً كأصول ثابتة، ويحقن إعلانات @font-face داخل الـ HTML بتاعك. صفر طلبات خارجية، صفر تزحزح في الـ layout، صفر Flash of Unstyled Text.

// app/layout.tsx
import { Inter, JetBrains_Mono } from "next/font/google";
import localFont from "next/font/local";

const inter = Inter({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-sans",
  preload: true,
});

const mono = JetBrains_Mono({
  subsets: ["latin"],
  display: "swap",
  variable: "--font-mono",
  preload: false, // not used above the fold
});

const arabic = localFont({
  src: "./fonts/Cairo-Variable.woff2",
  variable: "--font-arabic",
  display: "swap",
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${mono.variable} ${arabic.variable}`}>
      <body>{children}</body>
    </html>
  );
}

فيه مكسبين مش واضحين هنا. الأول، display: "swap" بيقول للمتصفح يرسم نص الـ fallback فوراً ويبدله لما الخط المخصص يوصل — مفيش نص مخفي. التاني، preload: false على الخطوط اللي مش مستخدمة فوق الـ fold بيمنع هدر bandwidth الـ preload. الافتراضي إنه بيعمل preload لكل حاجة، اللي ممكن فعلاً يأذي الـ LCP لو عندك أوزان خط كتيرة.

تقنية 4: اختيار استراتيجية الرندر الصح — SSG ولا ISR ولا SSR ولا PPR

جدال ISR ضد SSR في Next.js محسوم لمعظم حالات الاستخدام: ISR كسبان. لكن في 2026 عندنا خيار رابع — Partial Prerendering (PPR) — اللي بقى مستقر في Next.js 15.2+ وغيّر المعادلة بالكامل.

أنا بقرر كالتالي:

  1. SSG (Static Site Generation): استخدمها للمحتوى اللي بيتغير أقل من مرة في اليوم — صفحات تسويقية، توثيق، صفحات قانونية. TTFB هو أي حاجة الـ CDN بتاعك يقدر يقدمها، عادة أقل من 100 ميلي.
  2. ISR (Incremental Static Regeneration): استخدمها للمحتوى اللي بيتغير كل ساعة أو يومياً — مقالات المدونة، قوائم المنتجات، صفحات التصنيفات. حدد revalidate بالطزاجة اللي محتاجها فعلاً. ISR هي اللي بتشغّل أغلب مشاريع e-commerce اللي بنيتها؛ الغوص العميق في دليل تطوير مواقع التجارة الإلكترونية.
  3. SSR (Server-Side Rendering): استخدمها بس لما كل طلب فعلاً محتاج بيانات طازجة ومخصصة — لوحات تحكم لمستخدمين مسجلين، نتائج بحث بفلاتر خاصة بالمستخدم. SSR عندها أسوأ TTFB من أي استراتيجية ولازم تكون آخر حل.
  4. PPR (Partial Prerendering): استخدمها لما الصفحة بمعظمها ثابتة بس فيها جزيرة أو اتنين ديناميكيين — صفحة منتج بوصف ثابت وعدّاد مخزون ديناميكي، مثلاً. PPR بتقدم القشرة الثابتة فوراً من الـ CDN وتدفق الأجزاء الديناميكية. الأحسن في الاتنين.
// Static (default)
export default async function Page() { /* ... */ }

// ISR — revalidate every hour
export const revalidate = 3600;
export default async function Page() {
  const posts = await fetch("https://api.example.com/posts", {
    next: { revalidate: 3600 }
  }).then(r => r.json());
  return <PostList posts={posts} />;
}

// PPR — static shell, dynamic island
import { Suspense } from "react";
export const experimental_ppr = true;

export default function ProductPage({ params }) {
  return (
    <>
      <ProductDescription id={params.id} /> {/* static */}
      <Suspense fallback={<StockSkeleton />}>
        <LiveStockCounter id={params.id} /> {/* dynamic */}
      </Suspense>
    </>
  );
}
أرقام حقيقية من مشروع fintech كويتي (2025): هجرة صفحة هبوط لوحة تحكم من SSR كامل لـ PPR نزّلت TTFB من 740 ميلي لـ 90 ميلي (P75). LCP اتحسن من 3.1 ثانية لـ 1.4 ثانية. Lighthouse Performance طلع من 71 لـ 98. نفس الداتا، نفس الـ UX، بس استراتيجية رندر أذكى.

تقنية 5: تأجيل سكربتات Third-Party بـ next/script واستراتيجية Web Worker

سكربتات الـ third-party هي القاتل الصامت لسكورات Lighthouse. Google Tag Manager و HubSpot و Intercom و Hotjar و Facebook Pixel — كل واحد فيهم بيضيف 50-200 كيلوبايت من JavaScript حاجب و 200-500 ميلي ثانية شغل main-thread. شفت مواقع فيها 18 سكربت third-party وكود الموقع كان 80 كيلوبايت والـ trackers 1.2 ميجا.

مكون next/script بيديك أربع استراتيجيات تحميل. استخدمهم صح:

import Script from "next/script";

// Critical: load before page becomes interactive
<Script src="/critical-auth.js" strategy="beforeInteractive" />

// Default: load after page is interactive (use for most analytics)
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXX"
        strategy="afterInteractive" />

// Lazy: load when browser is idle (use for chat widgets, heatmaps)
<Script src="https://widget.intercom.io/widget/abc123"
        strategy="lazyOnload" />

// Worker (experimental, Partytown): run in web worker, off the main thread
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXX"
        strategy="worker" />

استراتيجية الـ worker بتغير اللعبة لـ INP. Partytown بينقل سكربتات الـ third-party لـ web worker عشان ما يقدروش يحجبوا الـ main thread أو الـ hydration بتاعتك. المقايضة: بعض السكربتات اللي بتعتمد على وصول مباشر للـ DOM (يا حبيب الإصدارات القديمة من Hotjar) مش هتشتغل. اختبر كويس قبل ما تشحن للإنتاج.

لو قست تكلفة JavaScript لموقع Next.js عادي، سكربتات الـ third-party تقريباً دايماً مسؤولة عن حجب الـ main thread أكتر من كود تطبيقك كله ضد بعض. دقّق فيهم بلا رحمة. أغلب الفِرَق ممكن يحذفوا نص سكربتات التتبع بتاعتهم ومش هيخسروا أي حاجة قابلة للقياس غير سكور Lighthouse البطيء.

تقنية 6: تقسيم الكود على مستوى الـ Route بـ next/dynamic وحدود Suspense

Next.js بيقسم الكود تلقائياً حسب الـ route، لكن جوه الـ route كل حاجة بتتجمع مع بعض. لو الصفحة الرئيسية بتاعتك بتستورد مكتبة رسوم بيانية 200 كيلوبايت بترسم تحت الـ fold بس، كل زائر بيدفع التكلفة دي — حتى اللي عمره ما هينزل. next/dynamic بيصلح ده بسيمانتيك React.lazy زائد تحكم في SSR.

import dynamic from "next/dynamic";

// Lazy-load a heavy chart, skip SSR
const RevenueChart = dynamic(() => import("./RevenueChart"), {
  ssr: false,
  loading: () => <div className="h-96 animate-pulse bg-gray-100" />,
});

// Lazy-load a modal — only when the user clicks
const SettingsModal = dynamic(() => import("./SettingsModal"));

export default function Dashboard() {
  const [showSettings, setShowSettings] = useState(false);
  return (
    <>
      <RevenueChart />
      <button onClick={() => setShowSettings(true)}>Settings</button>
      {showSettings && <SettingsModal onClose={() => setShowSettings(false)} />}
    </>
  );
}

أقرن الـ dynamic imports بحدود Suspense عشان تتحكم في حالات التحميل بدقة. القاعدة اللي بتبعها: كل مكون فوق 30 كيلوبايت بعد التصغير ومش ظاهر في الرندر الابتدائي لازم يتعمله dynamic import. شغّل @next/bundle-analyzer كل شهر وراقب الاعتماديات المفاجئة — لقيت moment.js و lodash كامل ومكتبات أيقونات مش مستخدمة مخبية في routes "حسيت إنها سريعة". تقليل حجم البندل بيتراكم: كل كيلوبايت بتقطعه بيساعد وقت التحليل، وقت التنفيذ، وضغط الذاكرة على الأجهزة الضعيفة.

تقنية 7: تفعيل Turbopack وضغط Brotli و Edge Runtime

Turbopack هو الـ bundler المبني بـ Rust بتاع Next.js، مستقر للـ dev في Next.js 15 ومستقر لبناءات الإنتاج في 15.4+. للتطوير المحلي هو 2-5 أضعاف أسرع من Webpack. لبناءات الإنتاج هو تقريباً مماثل لـ Webpack لكنه بينتج chunks أصغر بفضل heuristics tree-shaking أحسن. مكاسب أداء Turbopack بتظهر أكتر مع قواعد الكود الكبيرة — على تطبيق 600 ملف اشتغلت عليه، وقت الـ build نزل من 4 دقايق لـ 70 ثانية.

# next.config.js
const nextConfig = {
  experimental: {
    ppr: "incremental",
    optimizePackageImports: ["lucide-react", "date-fns", "@radix-ui/react-icons"],
  },
  compress: true, // Brotli + gzip
};

# package.json
"scripts": {
  "dev": "next dev --turbo",
  "build": "next build --turbo"
}

ضغط Brotli مفعل افتراضياً على Vercel وأغلب الـ hosts الحديثة. لو بتستضيف ذاتياً على Nginx، ضيف brotli on; brotli_types text/html text/css application/javascript application/json; للإعداد بتاعك. Brotli أصغر بـ 15-25% من gzip للأصول النصية — أداء ببلاش.

Edge Runtime بيشغّل كودك على شبكة edge بتاعة Vercel أو Cloudflare Workers، بـ cold starts تحت 50 ميلي وتوزيع عالمي. استخدمها للـ middleware، routes API بسيطة، وصفحات مش محتاجة Node.js APIs. ما تستخدمهاش لكل حاجة — Edge Runtime عندها سطح API أصغر وحد حجم كود 1 ميجا. قاعدتي: middleware دايماً على الـ edge، API routes على الـ edge لو بتستخدم fetch و crypto بس، الصفحات على الـ edge بس لو فعلاً بتستفيد من geo-routing.

Streaming SSR و React Suspense: إرسال HTML في أجزاء لـ TTFB أسرع

SSR التقليدي بيبني الـ HTML كاملاً على السيرفر، وبعدين يبعته كله مرة واحدة. لو أي جزء من الصفحة محتاج بيانات بطيئة، الاستجابة كلها بتتأخر. Streaming SSR مع Suspense بيقلب ده: السيرفر بيبعت الـ HTML الثابت فوراً، وبعدين يدفق الأجزاء الديناميكية لما تتحل.

import { Suspense } from "react";

export default function ProductPage({ params }) {
  return (
    <main>
      <Header /> {/* fast, sent immediately */}
      <ProductImages id={params.id} /> {/* fast */}

      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews productId={params.id} /> {/* slow, streams in */}
      </Suspense>

      <Suspense fallback={<RecommendationsSkeleton />}>
        <Recommendations productId={params.id} /> {/* slow, streams in */}
      </Suspense>
    </main>
  );
}

الـ TTFB للمستخدم هو وقت رسم القشرة — عادة تحت 100 ميلي. الأجزاء البطيئة بتدفق تدريجياً لما حدود Suspense تتحل. النمط ده لوحده ودّى منصة حجوزات إماراتية بنيتها من TTFB 1.8 ثانية لـ TTFB 140 ميلي من غير ما أغيّر استعلام واحد في قاعدة البيانات. Streaming SSR مع Suspense من أكتر الخصائص المهملة في App Router.

القضاء على Layout Shift: المساحة المحجوزة، نسب الأبعاد، و Fallbacks الخطوط

CLS بيبان حاجة صغيرة لحد ما تكتشف إنه 25% من سكور Performance في Lighthouse. كل بانر بيدفع المحتوى تحت، كل صورة بتتحمل متأخر، كل خط ويب بيعيد تدفق النص — كلهم بيتراكموا. الوصول لـ CLS أقل من 0.1 محتاج انضباط في كل طبقة.

الأربع قتلة CLS اللي بفحصهم الأول:

  • الصور من غير width و height: دايماً حدد الأبعاد أو استخدم next/image مع imports ثابتة. حتى الصور المتجاوبة محتاجة aspect ratio محجوزة بـ CSS.
  • الخطوط بتتبدل: استخدم next/font مع adjustFontFallback: true (الافتراضي). بيولد خط fallback متطابق عشان البدل ما يحركش الـ layout.
  • إعلانات أو embeds بتتحمل متأخر: احجز مساحة بحاوية min-height. لو الإعلان ما اتحملش، عندك مساحة فاضية — أحسن بكتير من تحريك المحتوى.
  • أنيميشن بيغيّر الـ layout: اعمل أنيميشن لـ transform و opacity، عمرك ما تعمل لـ width أو height أو top أو margin. خصائص الـ layout بتثير reflow؛ transform بتفضل على compositor الـ GPU.

تحسين تكلفة الـ Hydration: Islands Architecture و Selective Hydration في App Router

الـ Hydration هي العملية اللي React بيربط فيها event listeners بـ HTML المرسومة على السيرفر. ضرورية للتفاعلية، لكنها كمان من أكبر المساهمين في INP و Total Blocking Time. كل مكون تفاعلي على صفحتك بيضيف شغل hydration، وعلى الأجهزة البطيئة الشغل ده ممكن يحجب الـ main thread لمئات الميلي ثانية.

نموذج RSC في App Router هو في جوهره islands architecture — مكونات السيرفر جزر HTML ثابتة، مكونات الكلاينت جزر تفاعلية. Selective hydration معناها إن React بيعمل hydrate لمكونات الكلاينت بس، بالتوازي، بأولوية تفاعل المستخدم. لو مستخدم ضغط زرار لسه ما اتعملوش hydrate، React بيعمل له hydrate فوراً ويأجل الباقي.

عشان تقلل تكلفة الـ hydration: خلي مكونات الكلاينت صغيرة، تجنب تمرير props كبيرة من السيرفر للكلاينت (كل prop لازم يتسلسل ويتحلل تاني)، واستخدم React.memo لمكونات الكلاينت اللي بتعمل re-render كتير. لـ client state معقدة، فضّل Zustand أو Jotai على Redux — تكاليف إعداد أقل وبندلات أصغر. المبادئ هنا بتتداخل بشكل كبير مع اللي بغطيه في مقارنة React ضد Vue 2026 ودليلي عن بناء SaaS MVP بـ Laravel و React.

أخطاء أداء Next.js الشائعة اللي بتدمّر سكور Lighthouse

بعد ما دققت 40+ قاعدة كود Next.js، نفس الأنماط المضادة بتظهر مرة ورا التانية. ده الـ hit list بتاعي، مرتب حسب كتر ما بشوفه:

  1. تعليم الـ root layout بـ "use client". بيدفع التطبيق كله في بندل الكلاينت. الحل: خلي الـ layouts مكونات سيرفر.
  2. استخدام <img> بدل next/image. بيتخطى التحويل التلقائي للصيغة، التحجيم، و lazy loading. الحل: استخدم next/image في كل حتة ما عدا الأيقونات.
  3. تحميل مكتبات الأيقونات كـ default imports. import { Icon } from "react-icons" بيجر المكتبة كاملة. الحل: استورد الأيقونات اللي محتاجها بس أو استخدم optimizePackageImports.
  4. جلب البيانات في مكونات الكلاينت. بيسبب طلبات waterfall وبندلات كلاينت أكبر. الحل: اجلب في مكونات السيرفر ومرر البيانات تحت.
  5. نسيان loading.tsx. من غيرها، التنقل بيحس متجمد. الحل: ضيف skeleton عند كل route segment.
  6. شحن CSS مش مستخدم. Tailwind بيساعد بسبب JIT purging، لكن ملفات CSS مخصصة عادة فيها 80% قواعد ميتة. الحل: دقق بـ PurgeCSS أو استخدم Tailwind.
  7. عدم تعيين Cache-Control على الأصول الثابتة. Next.js بيعمل ده تلقائياً لـ /_next/static، لكن مش للملفات في /public. الحل: اضبط الـ CDN أو ضيف headers في next.config.js.
  8. تحميل Google Fonts عبر CDN link tags. عادة قديمة من Pages Router. الحل: دايماً استخدم next/font.
تحذير: ما تفعّلش كل علم تجريبي مع بعض. مرة فعّلت ppr و reactCompiler و turbo و after في نفس الوقت على build Next.js 15 canary. الـ build اتكسر، Vercel ما قدرش يـ deploy، وضيّعت نص يوم في الرجوع. فعّل ميزة تجريبية واحدة في المرة، deploy على preview branch، قس، وبعدين انتقل للي بعديها.

قياس أداء المستخدم الحقيقي بـ Vercel Speed Insights وبيانات CrUX

Lighthouse أداة معملية. المستخدمين الحقيقيين عايشين في عالم تاني: Wi-Fi ضعيف في الكافيهات، أجهزة Android ضعيفة، إضافات متصفح بتحقن JS، ad blockers، واتصالات 3G متقطعة. عشان تعرف اللي مستخدمينك بيختبروه فعلاً، محتاج RUM (Real User Monitoring).

Vercel Speed Insights هو الخيار الأسهل لو على Vercel — سطر واحد للتثبيت، تتبع Core Web Vitals تلقائي، تقسيم حسب الـ route والجهاز والدولة. الـ free tier بيغطي أغلب المواقع الصغيرة. للنشر الذاتي أو غير Vercel، باكدج web-vitals بيبعت المقاييس لأي endpoint عايزه.

// app/layout.tsx
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Analytics } from "@vercel/analytics/react";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <SpeedInsights />
        <Analytics />
      </body>
    </html>
  );
}

// Or send to your own endpoint
import { onLCP, onINP, onCLS } from "web-vitals/attribution";

onLCP((metric) => navigator.sendBeacon("/api/vitals", JSON.stringify(metric)));
onINP((metric) => navigator.sendBeacon("/api/vitals", JSON.stringify(metric)));
onCLS((metric) => navigator.sendBeacon("/api/vitals", JSON.stringify(metric)));

نقطة الدخول attribution دهب — بتقولك أنهي عنصر سبب LCP السيئ، أنهي event handler سبب INP السيء، وأنهي عقدة DOM اتزحزحت وسببت الـ CLS. من غير بيانات attribution إنت بتخمّن. بيها تقدر تصلح المشكلة الفعلية في دقايق.

دراسة حالة قبل/بعد: أخذ موقع Next.js من 62 لـ 100 في Lighthouse

السنة اللي فاتت دققت موقع تسويقي لـ SaaS B2B ألماني شغال على Next.js 14 Pages Router. السكورات الأولية على تدقيق موبايل: Performance 62، Accessibility 87، Best Practices 75، SEO 91. LCP كان 4.8 ثانية، TBT 920 ميلي، CLS 0.31. الفريق كان بيتجادل لستة شهور هل يعيد كتابته بـ Astro. الإسبويلر: ما احتاجوش.

اللي غيّرته، بالترتيب:

  1. هاجرت لـ App Router وخليت الصفحة الرئيسية مكون سيرفر (وفّرت 240 كيلوبايت من JS الكلاينت).
  2. استبدلت 14 تاج <img> بـ next/image، ضفت priority للـ hero (LCP نزل من 4.8 ثانية لـ 2.1 ثانية).
  3. حولت Google Fonts CDN لـ next/font (شيلت الطلب الحاجب للرندر، صلحت CLS المتعلق بـ FOUT).
  4. نقلت Google Tag Manager من afterInteractive لـ worker عبر Partytown (TBT نزل من 920 ميلي لـ 180 ميلي).
  5. عملت dynamic-import لكاروسيل الشهادات وحاسبة الأسعار (بندل الـ route نزل من 380 كيلوبايت لـ 95 كيلوبايت).
  6. فعّلت ISR مع revalidate: 3600 على كل الصفحات التسويقية (TTFB نزل من 680 ميلي لـ 80 ميلي).
  7. صلحت تلات صور ناقصة aspect ratios وبانر hero كان بيعمل أنيميشن لـ height بدل transform (CLS نزل من 0.31 لـ 0.04).

إجمالي الشغل: 11 ساعة على تلات أيام. Lighthouse موبايل النهائي: Performance 100، Accessibility 100، Best Practices 100، SEO 100. بيانات CrUX أظهرت LCP المستخدم الحقيقي بيتحسن من 4.2 ثانية P75 لـ 1.6 ثانية P75 في خلال أربع أسابيع. معدل التحويل على صفحة التسعير بتاعتهم طلع 18% خلال الشهر اللي بعد. ده الـ ROI بتاع تحسين أداء Next.js المعمول صح.

Checklist تحسين أداء Next.js (انسخها والصقها لتدقيقك اللي جاي)

اطبعها. الصقها على شاشتك. امشي عليها قبل كل إصدار.

  • ملفات الـ root layout والصفحة مكونات سيرفر (مفيش "use client" فوق).
  • كل الصور بتستخدم next/image مع sizes صريحة.
  • صورة الـ LCP عندها priority ومثالياً blur placeholder.
  • next.config.js بيفعّل AVIF في images.formats.
  • الخطوط بتتحمل عبر next/font، مش CDN link tags.
  • استراتيجية الرندر متاخدة بوعي لكل route (SSG أو ISR أو SSR أو PPR).
  • سكربتات الـ third-party بتستخدم next/script مع lazyOnload أو worker لما ممكن.
  • المكونات التقيلة تحت الـ fold بتتعمل dynamic import.
  • الـ bundle analyzer اتشغّل، مفيش اعتماديات مفاجئة فوق 50 كيلوبايت.
  • حدود Suspense حوالين أي جلب بيانات async بطيء.
  • كل route ديناميكي عنده skeleton في loading.tsx.
  • CLS مدقق — الصور محجمة، الخطوط بدلها مستقر، الأنيميشن على transform/opacity بس.
  • Vercel Speed Insights أو RUM مكافئ مثبت في الإنتاج.
  • ميزانية الأداء مفروضة في الـ CI.
  • ضغط Brotli مؤكد على مستوى الـ CDN.
  • headers Cache-Control متعينة على أصول /public.
  • الـ Middleware صغير وبيشتغل على Edge Runtime.

إمتى ما تلحقش سكور 100 — العوائد المتناقصة والمقايضات التجارية

هقول حاجة مش شعبية: ملاحقة 100 مش دايماً تستاهل. الانتقال من 60 لـ 90 بياخد كام يوم وبيديك أغلب فايدة الـ SEO والـ UX. الانتقال من 90 لـ 100 ممكن ياخد أسبوع تاني والمكاسب أغلبها تجميلية. لو فريقك صغير وبتشحن مزايا بتنمّي الإيراد، الانتقال من 95 لـ 100 نادراً يكون أعلى استخدام ROI لوقت الهندسة.

حالات معينة بقول فيها للعملاء يقفوا عند 90+:

  • لوحات تحكم بيانات تقيلة: رسوم بيانية فورية، جداول كبيرة، وفلاتر معقدة بشكل مشروع محتاجة JavaScript أكتر. 92 بـ UX رائع بيغلب 100 بمزايا معطلة.
  • مواقع بتعتمد على SDKs third-party: لو شغلك محتاج Intercom للدعم و Segment للتحليلات، تقدر تأجلهم لكن مش تقدر تحذفهم. اقبل خصم السكور.
  • MVPs مرحلة مبكرة: صادق على الشغل الأول. حسّن لما الترافيك يبرر الاستثمار. اختيارات الـ framework اللي بتأثر على الأداء طويل الأمد مغطاة في مقالتي عن WordPress ضد Laravel ومراجعتي لـ اتجاهات تطوير الويب في 2026.

الأداء ميزة، مش دين. عامله زي أي استثمار هندسي تاني: قس الأثر التجاري، رتّب أولويات التغييرات الأعلى تأثير، واقف لما المنحنى يبسط. نفس المنطق اللي بطبقه لما بنصح العملاء هل يعيّنوا مطور مستقل ضد وكالة بينطبق هنا — طابق المجهود مع العائد الفعلي.

المستقبل: React 19 Compiler و PPR Stable واللي جاي لأداء Next.js

تلات حاجات بيعيدوا تشكيل أداء Next.js في 2026 وبعدها. الأولى، React Compiler (مستقر في React 19.1) بيعمل auto-memoize للمكونات من غير ما تكتب useMemo أو useCallback. توقع تقليل 10-20% في شغل الـ re-render على مكونات الكلاينت المعقدة، وتحسينات INP ملموسة على واجهات تفاعلية.

التانية، Partial Prerendering بقى مستقر في Next.js 15.2. PPR هو نموذج الرندر اللي متوقع يسود بنهاية 2026 — قشرات ثابتة بجزر ديناميكية بتديك TTFB سريع زي الـ CDN ومحتوى مخصص من غير ما تختار بينهم.

التالتة، قواعد بيانات الـ edge (Vercel Postgres و Turso و driver الـ edge بتاع PlanetScale و driver Neon الـ serverless) بتقضي على تأخير round-trip قاعدة البيانات اللي كان بيخلي SSR بطيء. لما جلب البيانات بياخد 20 ميلي بدل 200 ميلي، تقدر تتحمل تعمل شغل رندر أكتر على السيرفر من غير ما تأذي TTFB. لمزيد عن إزاي جانب الـ back-end ده بيتجمع، شوف غوصي العميق في تصميم قواعد البيانات لتطبيقات الويب وأفضل ممارسات تصميم API في 2026.

لو بصينا أبعد: التحسين التدريجي بيعمل عودة. الـ frameworks بتدفع شغل أكتر للسيرفر وأقل للكلاينت. اللعبة النهائية مش بندل كلاينت أسرع — هي مفيش بندل كلاينت أصلاً للمحتوى اللي مش محتاج تفاعلية. Next.js في موضع كويس للمستقبل ده، لكن بس لو المطورين بطلوا يعاملوا كل مكون كأنه محتاج useState. قراءة ذات صلة: تحليلي لـ تطبيقات الويب التقدمية في 2026، وكتاب لعب تصميم الويب أولوية الموبايل، وchecklist أمان الموقع اللي كل تطبيق محسّن أداء لسه محتاجها.

أسئلة شائعة عن تحسين Lighthouse لـ Next.js

هل سكور Lighthouse 100 فعلاً قابل للتحقيق لموقع Next.js إنتاج حقيقي

أيوة، على أغلب المواقع المدفوعة بالمحتوى. شحنت عشرات المواقع التسويقية والمدونات ومتاجر e-commerce صغيرة على 100/100/100/100 على الموبايل. بيبقى أصعب للوحات تحكم معقدة أو مواقع بسكربتات third-party إلزامية. للحالات دي، 90-95 واقعي وممتاز برضو. الهدف لازم يكون عدّي Core Web Vitals — ده اللي Google بترتب عليه، مش رقم Lighthouse نفسه.

قد إيه بياخد تحسين موقع Next.js بطيء

لموقع تسويقي عادي أو مدونة: 1-3 أيام شغل مركّز للانتقال من 60 لـ 95+. لـ SaaS أو e-commerce معقد: 1-2 أسبوع، أحياناً أطول لو محتاج تعيد هيكلة أنماط جلب البيانات. أكبر المكاسب عادة بتيجي في أول 4 ساعات — الصور، الخطوط، وشيل مكونات الكلاينت غير الضرورية بتمثل 70% من أغلب تحسينات السكور. الـ 30% المتبقية هي الذيل الطويل لإصلاحات CLS، تأجيل الـ third-party، وتحليل البندل.

المفروض أستخدم Pages Router ولا App Router لأفضل أداء

App Router، في 2026، بدون منافسة. React Server Components و streaming SSR وحدود Suspense و Partial Prerendering كلهم مزايا App Router. Pages Router في وضع الصيانة — لسه شغال و Vercel مش هتكسره، لكن مفيش مزايا أداء جديدة بتنزل هناك. لو بتبدأ مشروع جديد، روح App Router. لو عندك قاعدة كود Pages Router كبيرة، هاجر تدريجياً — الراوترين يقدروا يتعايشوا في نفس المشروع.

هل استضافة Vercel فعلاً بتخلي Next.js أسرع من الاستضافة الذاتية

لأغلب الفِرَق، أيوة — لكن مش بسبب سحر. شبكة edge بتاعة Vercel عندها PoPs أكتر من أغلب الـ CDNs، تحسين الصور بتاعهم بيشتغل على الـ edge، وأدوات Speed Insights بتاعتهم متكاملة. الاستضافة الذاتية على Cloudflare أو إعداد Nginx+Node مضبوط ممكن يطابق أو يغلب Vercel على السرعة الخام، لكن هتقضي وقت هندسي كبير للوصول لده. لفريق عادي، Vercel هو الافتراضي الصح. لو حساس للسعر على نطاق واسع، الاستضافة الذاتية مع Cloudflare قدامها هي الخيار التاني الأفضل. بغطي المقايضات بالتفصيل في دليلي عن اختيار استضافة الويب في 2026.

إيه التغيير الواحد الأعلى تأثير اللي أقدر أعمله دلوقتي

لو هتعمل حاجة واحدة النهارده: دقق استخدامك لـ "use client". في تقريباً كل قاعدة كود Next.js بدققها، فيه مكونات كلاينت المفروض تكون مكونات سيرفر، وحدود كلاينت متحطة عالي في الشجرة. دفع "use client" تحت لمكونات الـ leaf بشكل روتيني بيقطع حجم بندل الكلاينت بـ 40-70%. ده لوحده بيحرّك LCP و TBT و INP في الاتجاه الصح في نفس الوقت.

إزاي أوازن بين سكور Lighthouse وسرعة التطوير والميزانية

حدد ميزانية أداء في بداية المشروع وافرضها في الـ CI. كده الأداء بيبقى قيد بيصمم المطورين جواه، مش refactor بتعمله في النهاية. اعمل الميزانية مرة، اشحن بسرعة للأبد. لفهم إزاي قرارات ميزانية الأداء بتأثر على تكلفة المشروع الكلية، شوف تحليلي لـ كام تكلفة موقع في 2026.

قد إيه لازم أعيد تدقيق الأداء بعد الإطلاق

اعمل Lighthouse CI تلقائي في خط النشر بتاعك عشان كل PR يظهر أثر الأداء. أبعد من ده، اعمل تدقيق يدوي عميق كل ربع سنة — الاعتماديات بتتحدث، المتصفحات بتتغير، بيانات CrUX بتاعتك بتتزحزح. المراقبة بالمستخدم الحقيقي لازم تشتغل مستمرة. الفِرَق اللي بتحافظ على أداء عظيم على المدى الطويل هم اللي بيعاملوه كصيانة مستمرة، مش مشروع لمرة واحدة.

جاهز تخلي تطبيق Next.js بتاعك سريع فعلاً

الأداء هو الشغل الهندسي الأعلى ROI اللي أغلب الفِرَق بيتجاهلوه. كل 100 ميلي بتقطعها من LCP بتزيد التحويلات بشكل قابل للقياس. كل نسبة مئوية بتشيلها من بندلك بتحسّن التفاعل على الشبكات البطيئة. وكل نقطة Lighthouse بتكسبها بتساعد SEO بتاعك يتراكم شهر ورا شهر. لو عايز تدقيق حقيقي — مش checklist عامة "استخدم next/image"، لكن مراجعة على مستوى الكود بإصلاحات مرتبة بأولوية حسب التأثير — أنا بشتغل مع فِرَق حوالين العالم لشحن تطبيقات Next.js توصل 95+ Lighthouse على الموبايل وتفضل هناك. اتواصل لاستشارة أداء مجانية 30 دقيقة وأنا هراجع موقعك المباشر، أحدد التلات تغييرات الأعلى تأثير، وأقولك بصراحة هل محتاج مساعدة من بره ولا سبرنت مركّز. تقدر كمان تتصفح قائمة خدماتي الكاملة عشان تشوف إزاي بساعد الفِرَق تشحن تطبيقات ويب أسرع، أخف، وأكتر ربحية.

كلمات مفتاحية: Next.jsReactperformanceLighthouseSEO

مستعد لتطبيق ما قرأته؟

استشاره مجانية 30 دقيقة، رد خلال 24 ساعة، وعرض سعر مكتوب.

تواصل واتساب