أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

أتمتة توليد Open Graph images لكل مقال بـ Satori — صور SEO احترافية بدون Figma

📅 ٢٠ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
أتمتة توليد Open Graph images لكل مقال بـ Satori — صور SEO احترافية بدون Figma

لو كل ما تكتب مقال بتضطر تفتح Figma وتصمم صورة Open Graph من الصفر، إنت بتحرق 12–15 دقيقة في كل مقال. في 200 مقال، ده يساوي 40+ ساعة شغل ضايعة في بكسلات. المقال ده بيوريك إزاي تبني pipeline يولّد صورة OG احترافية لكل مقال بصفر تدخّل يدوي، مستخدمًا Satori من Vercel.

أتمتة صور Open Graph بـ Satori: الفكرة الكاملة

الحل هنا مش إضافة، ده تغيير كامل لطريقة الشغل. بدل ما تصمم الصورة، إنت بتبرمج قالب مرة واحدة — وبعدها كل مقال بياخد صورته منه تلقائيًا.

المشكلة باختصار

صورة Open Graph هي اللي بتظهر لمّا حد يشارك رابط مقالك على تويتر أو لينكدإن أو واتساب. لو ماكانتش موجودة، الرابط بيظهر أصلع وبيقل معدل النقر بنسبة 35% تقريبًا بناءً على أرقام Backlinko. لو كانت موجودة وحلوة، الـ CTR بيعلى. الطرق الشائعة لتوليدها كلها فيها مشكلة:

  • Figma يدوي: بيشتغل لـ 5 مقالات في الأسبوع، بيقع لو بتنشر يوميًا.
  • Canva templates: أسرع من Figma بس لسه بيحتاج فتح متصفح + تعديل + تصدير + رفع.
  • headless Chrome (Puppeteer): بطيء (3–5 ثواني لكل صورة) ومكلف في الـ CI (يحتاج 1GB RAM).
هاتف يعرض معاينة بطاقة Open Graph لمقال على منصة تواصل اجتماعي

إيه هو Satori؟ مثال بسيط الأول

تخيّل إنك عندك آلة صناعة بطاقات معايدة. الآلة دي بتاخد منك قالب ورقي متقطّع (زي استنسل)، وبتختم عليه الاسم والتاريخ والرسمة — وطلعت بطاقة كاملة في ثانيتين. إنت ما بترسمش البطاقة، إنت بس بتقولها "الاسم محمد" و"العيد الفطر" وهي بتلصق الباقي. Satori بيشتغل بالظبط كده، بس بدل البطاقة الورقية، قالبك هو كود JSX، والمدخل بتاعك هو بيانات المقال (العنوان + الاسم + التاريخ).

علميًا: Satori مكتبة JavaScript من فريق Vercel، بتاخد كود JSX + CSS وبتحوّله لـ SVG مباشرة بدون ما تفتح متصفح. بعد كده ممكن تحوّل الـ SVG لـ PNG بمكتبة @resvg/resvg-js. الفرق الأهم عن Puppeteer: مفيش browser engine، فالعملية بتاخد ~80 مللي ثانية بدل 3 ثواني.

التثبيت وإعداد التيمبليت

الحزم المطلوبة اتنين بس. ثبّتهم:

Bash
npm install satori @resvg/resvg-js
# optional: لو عايز خطوط عربية جميلة
npm install @fontsource/cairo

دلوقتي اكتب القالب. افرض عندك مقال عنوانه "أتمتة تنظيف Docker" ومكتوب في 2026. القالب هيستقبل العنوان والتاريخ كـ props ويرجّع JSX:

TSX
// og-template.tsx
export const OGTemplate = ({ title, date, author }) => ({
  type: 'div',
  props: {
    style: {
      width: 1200,
      height: 630,
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'space-between',
      padding: '80px',
      background: 'linear-gradient(135deg, #0f172a, #1e40af)',
      color: 'white',
      fontFamily: 'Cairo',
    },
    children: [
      {
        type: 'div',
        props: {
          style: { fontSize: 28, opacity: 0.7 },
          children: 'ahmedhaies.com',
        },
      },
      {
        type: 'div',
        props: {
          style: { fontSize: 64, fontWeight: 700, lineHeight: 1.2 },
          children: title,
        },
      },
      {
        type: 'div',
        props: {
          style: { fontSize: 26, opacity: 0.6 },
          children: `${author} · ${date}`,
        },
      },
    ],
  },
});

عندنا هنا تصميم بسيط: خلفية متدرّجة، اسم الموقع فوق، العنوان الضخم في النص، التاريخ تحت. كل ده يتعدّل من مكان واحد.

سكربت التوليد

ده السكربت اللي بياخد بيانات المقال ويطلع PNG:

JavaScript
// generate-og.mjs
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
import fs from 'node:fs';
import { OGTemplate } from './og-template.js';

const cairoRegular = fs.readFileSync('./fonts/Cairo-Regular.ttf');
const cairoBold = fs.readFileSync('./fonts/Cairo-Bold.ttf');

export async function generateOG({ title, date, author, slug }) {
  const svg = await satori(
    OGTemplate({ title, date, author }),
    {
      width: 1200,
      height: 630,
      fonts: [
        { name: 'Cairo', data: cairoRegular, weight: 400 },
        { name: 'Cairo', data: cairoBold, weight: 700 },
      ],
    }
  );

  const png = new Resvg(svg).render().asPng();
  fs.writeFileSync(`./public/og/${slug}.png`, png);
  console.log(`✓ ${slug}.png (${(png.length / 1024).toFixed(1)} KB)`);
}

الصورة النهائية بتطلع ~180 كيلوبايت في المتوسط، بمقاس 1200×630 (المقاس الرسمي لـ Facebook و Twitter Cards).

محرر كود يعرض تيمبليت JSX لتوليد صور Open Graph باستخدام Satori

التكامل مع GitHub Actions

الفكرة: كل مرة تـ push مقال جديد، الـ workflow يكتشف الملف الجديد، يشغّل السكربت، ويعمل commit بالصورة. ده ملف .github/workflows/og.yml:

YAML
name: Generate OG Images
on:
  push:
    paths: ['content/posts/**.md']

jobs:
  og:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: node scripts/build-all-og.mjs
      - name: Commit generated images
        run: |
          git config user.name "og-bot"
          git config user.email "og-bot@users.noreply.github.com"
          git add public/og/
          git diff --staged --quiet || git commit -m "chore: regenerate OG images"
          git push

السكربت build-all-og.mjs بيقرأ الـ frontmatter من كل ملف Markdown، بيستدعي generateOG لكل واحد، ويخطّي اللي عنده الصورة متولّدة فعلًا (بمقارنة hash للعنوان).

أرقام حقيقية من تجربة فعلية

  • زمن التوليد: 80 مللي ثانية لكل صورة على GitHub runner عادي.
  • توليد 180 صورة دفعة واحدة: 14 ثانية (معظمها npm install).
  • حجم الصورة: 160–220 كيلوبايت (أقل من 1/3 ما كانت بتطلعه Figma الأصلي عندي بـ 650KB).
  • وقت الكتّاب المحرّر: من 15 دقيقة/مقال إلى صفر. في 200 مقال = 50 ساعة.
  • زمن الـ CI: 42 ثانية لكل push (بدل 3–4 دقائق لو كنت بتستخدم Puppeteer).
مجموعة بطاقات تصميم ملوّنة متناسقة تمثل صور Open Graph مولّدة تلقائيًا

Trade-offs وما يجب الانتباه له

الحل ده مش مجاني الكلفة. الأمانة تقتضي الأرقام دي:

  • CSS المدعوم محدود: Satori بيدعم subset من CSS بس. نسيت float، position: absolute بتصرفات معينة، و backdrop-filter. اعتمد على flexbox خالص.
  • مفيش JavaScript runtime: ممنوع useState أو useEffect. القوالب لازم تكون pure functions بترجّع JSX.
  • الخطوط بتتحمّل في الذاكرة: كل خط بيزوّد استهلاك الـ RAM بـ 5–15 ميجا. لو عندك 4 أوزان، احسبها.
  • الافتراض إن: الإعداد ده مبني على أن عندك أقل من 500 مقال وتحدّث أقل من 10 ملفات Markdown في الـ push الواحد. فوق كده ابدأ تفكّر في on-demand generation بدل batch.

متى لا تستخدم Satori

الطريقة دي بتفشل في الحالات دي:

  • تصاميم بصرية معقّدة جدًا: لو قالبك محتاج تأثيرات Figma متقدّمة (glassmorphism حقيقي، 3D transforms). افتح Figma وصدّر يدويًا.
  • محتوى RTL مختلط مع LTR في نفس السطر: الدعم لسه مش كامل في Satori 0.10.x. اختبر قبل ما تعتمد.
  • لو الصورة بتتغيّر بناءً على بيانات real-time: ساعتها استخدم Vercel OG API مباشرة (edge function) بدل الـ batch build.

المصادر

  • مستودع Satori الرسمي على GitHub — المرجع الأساسي للمكتبة وملف README الشامل.
  • وثائق Vercel لتوليد OG Images — تفاصيل الـ API والقيود.
  • مكتبة resvg-js — المسؤولة عن تحويل SVG إلى PNG.
  • دراسة Backlinko عن تأثير Open Graph على CTR — مصدر رقم الـ 35%.
  • Facebook Sharing Best Practices — المقاس الرسمي 1200×630.

الخطوة التالية

افتح المقال اللي نشرته آخر مرة، شوف إيه صورة Open Graph بتاعته (جرّبها على opengraph.xyz). لو مفيش صورة أو الصورة مش متّسقة، ابدأ بتيمبليت Satori واحد فقط لنوع المحتوى الأكثر نشرًا عندك. بعد ما يشتغل لـ 10 مقالات، زوّد المتغيّرات. متحاولش تبني 5 قوالب من أول يوم — هتسيب المشروع.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة