كيفية إضافة بيانات وصفية وإنشاء صور OG

يمكن استخدام واجهات برمجة تطبيقات البيانات الوصفية (Metadata APIs) لتعريف بيانات التطبيق الوصفية لتحسين تحسين محركات البحث (SEO) وقابلية المشاركة على الويب، وتشمل:

  1. كائن metadata الثابت
  2. دالة generateMetadata الديناميكية
  3. اتفاقيات ملفات خاصة يمكن استخدامها لإضافة أيقونات المفضلة وصور OG ثابتة أو مُنشأة ديناميكيًا.

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

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

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

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

يمكن تعريف حقول البيانات الوصفية الأخرى باستخدام كائن Metadata (لـالبيانات الوصفية الثابتة) أو دالة generateMetadata (لـالبيانات الوصفية المُنشأة).

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

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

import type { Metadata } from 'next'

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

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

export default function Page() {}

يمكنك عرض قائمة كاملة بالخيارات المتاحة في توثيق generateMetadata.

البيانات الوصفية المُنشأة

يمكنك استخدام دالة generateMetadata لجلب البيانات الوصفية التي تعتمد على البيانات. على سبيل المثال، لجلب العنوان والوصف لمنشور مدونة معين:

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  const slug = (await params).slug

  // جلب معلومات المنشور
  const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
    res.json()
  )

  return {
    title: post.title,
    description: post.description,
  }
}

export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
  const slug = (await params).slug

  // جلب معلومات المنشور
  const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
    res.json()
  )

  return {
    title: post.title,
    description: post.description,
  }
}

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

خلف الكواليس، سيقوم Next.js ببث البيانات الوصفية بشكل منفصل عن واجهة المستخدم وحقنها في HTML بمجرد حلها.

تخزين طلبات البيانات مؤقتًا

قد تكون هناك حالات تحتاج فيها إلى جلب نفس البيانات لكل من البيانات الوصفية والصفحة نفسها. لتجنب الطلبات المكررة، يمكنك استخدام دالة cache في React لتخزين القيمة المُرجعة وجلب البيانات مرة واحدة فقط. على سبيل المثال، لجلب معلومات منشور المدونة لكل من البيانات الوصفية والصفحة:

import { cache } from 'react'
import { db } from '@/app/lib/db'

// سيتم استخدام getPost مرتين، ولكن تنفيذها مرة واحدة فقط
export const getPost = cache(async (slug: string) => {
  const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
  return res
})
import { cache } from 'react'
import { db } from '@/app/lib/db'

// سيتم استخدام getPost مرتين، ولكن تنفيذها مرة واحدة فقط
export const getPost = cache(async (slug) => {
  const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
  return res
})
import { getPost } from '@/app/lib/data'

export async function generateMetadata({
  params,
}: {
  params: { slug: string }
}) {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.description,
  }
}

export default async function Page({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <div>{post.title}</div>
}
import { getPost } from '@/app/lib/data'

export async function generateMetadata({ params }) {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.description,
  }
}

export default async function Page({ params }) {
  const post = await getPost(params.slug)
  return <div>{post.title}</div>
}

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

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

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

أيقونات المفضلة (Favicons)

أيقونات المفضلة هي أيقونات صغيرة تمثل موقعك في الإشارات المرجعية ونتائج البحث. لإضافة أيقونة مفضلة إلى تطبيقك، أنشئ ملف favicon.ico وأضفه إلى مجلد التطبيق الجذر.

ملف Favicon الخاص داخل مجلد App مع ملفات layout و page المجاورة

يمكنك أيضًا إنشاء أيقونات مفضلة برمجيًا باستخدام الكود. راجع توثيق favicon لمزيد من المعلومات.

صور Open Graph الثابتة

صور Open Graph (OG) هي صور تمثل موقعك في وسائل التواصل الاجتماعي. لإضافة صورة OG ثابتة إلى تطبيقك، أنشئ ملف opengraph-image.png في مجلد التطبيق الجذر.

ملف صورة OG الخاص داخل مجلد App مع ملفات layout و page المجاورة

يمكنك أيضًا إضافة صور OG لمسارات محددة عن طريق إنشاء ملف opengraph-image.png أعمق في هيكل المجلد. على سبيل المثال، لإنشاء صورة OG خاصة بمسار /blog، أضف ملف opengraph-image.jpg داخل مجلد blog.

ملف صورة OG الخاص داخل مجلد blog

ستأخذ الصورة الأكثر تحديدًا الأسبقية على أي صور OG فوقها في هيكل المجلد.

يتم دعم تنسيقات الصور الأخرى مثل jpeg و png و webp. راجع توثيق Open Graph Image لمزيد من المعلومات.

صور Open Graph المُنشأة

يسمح لك منشئ ImageResponse بإنشاء صور ديناميكية باستخدام JSX و CSS. هذا مفيد لصور OG التي تعتمد على البيانات.

على سبيل المثال، لإنشاء صورة OG فريدة لكل منشور مدونة، أضف ملف opengraph-image.ts داخل مجلد blog، واستورد منشئ ImageResponse من next/og:

import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'

// بيانات الصورة
export const size = {
  width: 1200,
  height: 630,
}

export const contentType = 'image/png'

// إنشاء الصورة
export default async function Image({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)

  return new ImageResponse(
    (
      // عنصر JSX لـ ImageResponse
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {post.title}
      </div>
    )
  )
}
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'

// بيانات الصورة
export const size = {
  width: 1200,
  height: 630,
}

export const contentType = 'image/png'

// إنشاء الصورة
export default async function Image({ params }) {
  const post = await getPost(params.slug)

  return new ImageResponse(
    (
      // عنصر JSX لـ ImageResponse
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {post.title}
      </div>
    )
  )
}

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

ملاحظة جيدة:

  • تتوفر أمثلة في Vercel OG Playground.
  • يستخدم ImageResponse @vercel/og، وsatori، و resvg لتحويل HTML و CSS إلى PNG.
  • يتم دعم flexbox ومجموعة فرعية من خصائص CSS فقط. التخطيطات المتقدمة (مثل display: grid) لن تعمل.