البيانات الوصفية (Metadata)

يقدم Next.js واجهة برمجة تطبيقات للبيانات الوصفية (Metadata API) يمكن استخدامها لتعريف بيانات وصفية لتطبيقك (مثل علامات meta و link داخل عنصر head في HTML) لتحسين تحسين محركات البحث (SEO) وإمكانية المشاركة على الويب.

هناك طريقتان لإضافة بيانات وصفية لتطبيقك:

  • البيانات الوصفية المعتمدة على التكوين: قم بتصدير كائن metadata ثابت أو دالة generateMetadata ديناميكية في ملف layout.js أو page.js.
  • البيانات الوصفية المعتمدة على الملفات: أضف ملفات خاصة ثابتة أو مُنشأة ديناميكيًا إلى أجزاء المسار.

مع هذين الخيارين، سيقوم Next.js تلقائيًا بإنشاء عناصر <head> ذات الصلة لصفحاتك. يمكنك أيضًا إنشاء صور OG ديناميكية باستخدام منشئ ImageResponse.

البيانات الوصفية الثابتة

لتعريف بيانات وصفية ثابتة، قم بتصدير كائن Metadata من ملف layout.js أو ملف page.js ثابت.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}
export const metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

لجميع الخيارات المتاحة، راجع مرجع API.

البيانات الوصفية الديناميكية

يمكنك استخدام دالة generateMetadata لجلب بيانات وصفية تتطلب قيمًا ديناميكية.

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // قراءة معلمات المسار
  const id = params.id

  // جلب البيانات
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // الوصول الاختياري إلى البيانات الوصفية الأصلية وتوسيعها (بدلاً من استبدالها)
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
  // قراءة معلمات المسار
  const id = params.id

  // جلب البيانات
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // الوصول الاختياري إلى البيانات الوصفية الأصلية وتوسيعها (بدلاً من استبدالها)
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }) {}

لجميع المعلمات المتاحة، راجع مرجع API.

ملاحظة جيدة:

  • يتم دعم كل من البيانات الوصفية الثابتة والديناميكية عبر generateMetadata فقط في مكونات الخادم (Server Components).
  • طلبات fetch يتم تخزينها مؤقتًا تلقائيًا لنفس البيانات عبر generateMetadata وgenerateStaticParams والتخطيطات والصفحات ومكونات الخادم. يمكن استخدام cache من React إذا لم يكن fetch متاحًا.
  • سينتظر Next.js حتى يكتمل جلب البيانات داخل generateMetadata قبل بث واجهة المستخدم إلى العميل. وهذا يضمن أن الجزء الأول من الاستجابة المبثوثة يتضمن علامات <head>.

البيانات الوصفية المعتمدة على الملفات

هذه الملفات الخاصة متاحة للبيانات الوصفية:

يمكنك استخدام هذه الملفات للبيانات الوصفية الثابتة، أو يمكنك إنشاء هذه الملفات برمجيًا باستخدام التعليمات البرمجية.

للتنفيذ والأمثلة، راجع مرجع ملفات البيانات الوصفية وإنشاء الصور الديناميكية.

السلوك

البيانات الوصفية المعتمدة على الملفات لها أولوية أعلى وستتجاوز أي بيانات وصفية معتمدة على التكوين.

الحقول الافتراضية

هناك علامتان meta افتراضيتان تتم إضافتهما دائمًا حتى إذا لم يحدد المسار بيانات وصفية:

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

ملاحظة جيدة: يمكنك تجاوز علامة viewport الافتراضية.

الترتيب

يتم تقييم البيانات الوصفية بالترتيب، بدءًا من جزء الجذر وصولاً إلى الجزء الأقرب إلى جزء page.js النهائي. على سبيل المثال:

  1. app/layout.tsx (تخطيط الجذر)
  2. app/blog/layout.tsx (تخطيط المدونة المتداخل)
  3. app/blog/[slug]/page.tsx (صفحة المدونة)

الدمج

بعد ترتيب التقييم، يتم دمج كائنات البيانات الوصفية المصدرة من أجزاء متعددة في نفس المسار بشكل سطحي معًا لتشكيل مخرجات البيانات الوصفية النهائية للمسار. يتم استبدال المفاتيح المكررة بناءً على ترتيبها.

هذا يعني أن البيانات الوصفية مع الحقول المتداخلة مثل openGraph وrobots التي يتم تعريفها في جزء سابق يتم تجاوزها بواسطة الجزء الأخير الذي يحددها.

تجاوز الحقول

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// المخرجات:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

في المثال أعلاه:

  • يتم استبدال title من app/layout.js بـ title في app/blog/page.js.
  • يتم استبدال جميع حقول openGraph من app/layout.js في app/blog/page.js لأن app/blog/page.js يحدد بيانات openGraph. لاحظ غياب openGraph.description.

إذا كنت ترغب في مشاركة بعض الحقول المتداخلة بين الأجزاء أثناء تجاوز البعض الآخر، يمكنك استخراجها إلى متغير منفصل:

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

في المثال أعلاه، يتم مشاركة صورة OG بين app/layout.js وapp/about/page.js بينما تختلف العناوين.

وراثة الحقول

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/about/page.js
export const metadata = {
  title: 'About',
}

// المخرجات:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

ملاحظات

  • يتم استبدال title من app/layout.js بـ title في app/about/page.js.
  • يتم وراثة جميع حقول openGraph من app/layout.js في app/about/page.js لأن app/about/page.js لا يحدد بيانات openGraph.

إنشاء الصور الديناميكية

يسمح منشئ ImageResponse بإنشاء صور ديناميكية باستخدام JSX و CSS. هذا مفيد لإنشاء صور وسائل التواصل الاجتماعي مثل صور Open Graph وبطاقات Twitter وغيرها.

يستخدم ImageResponse Edge Runtime، ويقوم Next.js تلقائيًا بإضافة العناوين الصحيحة للصور المخزنة مؤقتًا عند الحافة، مما يساعد في تحسين الأداء وتقليل إعادة الحساب.

لاستخدامه، يمكنك استيراد ImageResponse من next/server:

app/about/route.js
import { ImageResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}

يتكامل ImageResponse جيدًا مع واجهات برمجة تطبيقات Next.js الأخرى، بما في ذلك Route Handlers والبيانات الوصفية المعتمدة على الملفات. على سبيل المثال، يمكنك استخدام ImageResponse في ملف opengraph-image.tsx لإنشاء صور Open Graph أثناء وقت البناء أو ديناميكيًا عند وقت الطلب.

يدعم ImageResponse خصائص CSS الشائعة بما في ذلك flexbox والتحديد المطلق والخطوط المخصصة ولف النص والمركزة والصور المتداخلة. راجع القائمة الكاملة لخصائص CSS المدعومة.

ملاحظة جيدة:

  • تتوفر أمثلة في Vercel OG Playground.
  • يستخدم ImageResponse @vercel/og وSatori وResvg لتحويل HTML وCSS إلى PNG.
  • يتم دعم Edge Runtime فقط. لن يعمل Node.js Runtime الافتراضي.
  • يتم دعم flexbox ومجموعة فرعية من خصائص CSS فقط. لن تعمل التخطيطات المتقدمة (مثل display: grid).
  • الحد الأقصى لحجم الحزمة هو 500KB. يشمل حجم الحزمة JSX وCSS والخطوط والصور وأي أصول أخرى. إذا تجاوزت الحد، ففكر في تقليل حجم أي أصول أو جلبها في وقت التشغيل.
  • يتم دعم تنسيقات الخطوط ttf وotf وwoff فقط. لتحقيق أقصى سرعة لتحليل الخط، يُفضل استخدام ttf أو otf بدلاً من woff.

JSON-LD

JSON-LD هو تنسيق للبيانات المنظمة التي يمكن استخدامها بواسطة محركات البحث لفهم محتواك. على سبيل المثال، يمكنك استخدامه لوصف شخص أو حدث أو منظمة أو فيلم أو كتاب أو وصفة والعديد من أنواع الكيانات الأخرى.

توصيتنا الحالية لـ JSON-LD هي تقديم البيانات المنظمة كعلامة <script> في مكونات layout.js أو page.js. على سبيل المثال:

export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* إضافة JSON-LD إلى صفحتك */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}
export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* إضافة JSON-LD إلى صفحتك */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

يمكنك التحقق من صحة بياناتك المنظمة واختبارها باستخدام Rich Results Test من Google أو Schema Markup Validator العام.

يمكنك كتابة JSON-LD باستخدام TypeScript باستخدام حزم المجتمع مثل schema-dts:

import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}