اولین باری که یک بلوک سفارشی نوشتم، چند ساعت طول کشید تا بفهمم چرا بلوک در ویرایشگر ظاهر نمی‌شود. بعد از چند بار بازبینی، کشف کردم فایل block.json را در جای اشتباهی قرار داده‌ام و وردپرس آن را پیدا نمی‌کرد. از آن روز، ساختار بلوک را مثل یک نقشه مشخص در ذهن دارم: هر فایل سر جای خودش، هر هوک در زمان خودش. این مقاله، همان نقشه است که در سال‌های گذشته روی ده‌ها پروژه اجرایش کرده‌ام.

بلوک سفارشی چرا و کجا لازم می‌شود؟

در پروژه‌های وردپرسی، بلوک سفارشی معمولاً از دو جا سر درمی‌آورد: یا مشتری می‌خواهد یک بخش مشخص با ساختار ثابت در نوشته‌ها تکرار شود (مثل کارت محصول، جعبه اطلاعات، یا فرم تماس سریع) و شما نمی‌خواهید آن را دستی در هر نوشته بازسازی کنید؛ یا افزونه‌ای در اختیار دارید که می‌خواهید خروجی‌اش را مستقیماً در ویرایشگر قابل استفاده کنید. در هر دو حالت، بلوک سفارشی ابزار درستی است.

در نگاه اول ممکن است فکر کنید این کار با یک shortcode هم انجام می‌شود، ولی در تجربه من بلوک در چند سناریو مزیت جدی دارد: تعامل بصری زنده با کاربر در ویرایشگر، امکان پیش‌نمایش بدون خروج از صفحه ویرایش، سازگاری با سایت ویرایشگر و ذخیره‌سازی داده به‌صورت ساختاریافته در HTML. اگر با مفهوم شورت‌کد آشنا نیستید، ساخت شورت‌کد با کدنویسی وردپرس تفاوت این دو ابزار را دقیق‌تر نشان می‌دهد.

بلوک گوتنبرگ فقط یک قطعه رابط کاربری نیست؛ یک قرارداد است بین ویرایشگر، دیتابیس و فرانت‌اند. اگر این قرارداد درست بسته نشود، هر سه طرف ضرر می‌کنند.

آناتومی یک بلوک گوتنبرگ

پیش از کد، ساختار فایل‌های یک بلوک سفارشی را بشناسید. در رویکرد مدرن وردپرس، بلوک‌ها با فایل block.json تعریف می‌شوند و بقیه فایل‌ها حول همین تعریف می‌چرخند:

فایلنقشاجباری؟
block.jsonتعریف هویت، attribute‌ها و تنظیمات بلوکبله (رویکرد مدرن)
src/index.jsثبت بلوک در جاوااسکریپتبله
src/edit.jsرابط ویرایشگر بلوکبله
src/save.jsخروجی ذخیره‌شده در دیتابیسبرای بلوک ثابت، بله
src/editor.scssاستایل مخصوص ویرایشگراختیاری
src/style.scssاستایل مشترک ویرایشگر و فرانت‌انداختیاری
render.phpخروجی بلوک پویا در سمت سرورفقط برای بلوک پویا

اگر با ساختار فایل قالب و افزونه آشنا نیستید، راهنمای توسعه افزونه وردپرس از صفر و راهنمای توسعه قالب وردپرس از صفر نقطه شروع خوبی هستند. برای درک هوک‌ها که در ساخت بلوک زیاد به‌کار می‌آیند، هوک‌های وردپرس چیستند و چگونه کار می‌کنند پیش‌نیاز ضروری است.

گام اول: تعریف block.json

فایل block.json شناسنامه بلوک شماست. وردپرس با خواندن این فایل، بلوک را می‌شناسد، attribute‌هایش را می‌فهمد و می‌داند استایل‌ها و اسکریپت‌هایش را چطور بارگذاری کند. نمونه‌ای ساده از این فایل برای یک بلوک کارت اطلاعات:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "myplugin/info-card",
  "version": "1.0.0",
  "title": "کارت اطلاعات",
  "category": "widgets",
  "icon": "info-outline",
  "description": "یک کارت برای نمایش اطلاعات کوتاه",
  "textdomain": "myplugin",
  "attributes": {
    "title": {
      "type": "string",
      "default": ""
    },
    "content": {
      "type": "string",
      "default": ""
    }
  },
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css"
}

سه نکته مهم در این فایل: اول، name باید با namespace معتبر و یکتا باشد؛ همین نام در دیتابیس و در ویرایشگر به‌عنوان شناسه استفاده می‌شود. دوم، apiVersion روی ۳ یعنی از بلوک‌های نسل جدید استفاده می‌کنید که پیشنهادشده است. سوم، attributes شبیه schema داده‌ای بلوک است؛ این‌جا هر چیزی که می‌خواهید در دیتابیس ذخیره کنید تعریف می‌کنید.

گام دوم: ثبت بلوک در PHP

بلوک‌ها معمولاً در افزونه سفارشی ثبت می‌شوند، نه در قالب؛ چون بلوک به‌ذات یک قابلیت محتوایی است، نه یک ویژگی ظاهری. الگوی ثبت در فایل اصلی افزونه:

<?php
function myplugin_register_blocks() {
    register_block_type( __DIR__ . '/build/info-card' );
}
add_action( 'init', 'myplugin_register_blocks' );

اگر بلوک را برای قالب می‌نویسید، همین کد را در functions.php قالب چایلد قرار دهید. نکته مهم: register_block_type باید در هوک init فراخوانی شود؛ اگر در after_setup_theme یا در بالای فایل بدون هوک قرار دهید، بلوک در ویرایشگر ظاهر نمی‌شود. اگر با تفاوت هوک‌ها آشنا نیستید، ساخت اکشن سفارشی در وردپرس ترتیب درست اجرا را توضیح می‌دهد.

اگر رویکرد بدون build tools را ترجیح می‌دهید — مثلاً برای پروژه‌های کوچک یا آموزش — می‌توانید فایل‌های JS را مستقیم در wp-content/plugins بگذارید و به‌جای file:./index.js از editorScript: "myplugin-info-card-editor" استفاده کنید. من در پروژه‌های جدی همیشه از build tools استفاده می‌کنم چون نگهداری و نسخه‌بندی را ساده‌تر می‌کند.

گام سوم: ثبت جاوااسکریپتی بلوک

در فایل src/index.js بلوک را در ویرایشگر ثبت می‌کنید. الگوی ساده:

import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import Save from './save';
import metadata from './block.json';
import './editor.scss';
import './style.scss';

registerBlockType( metadata.name, {
    edit: Edit,
    save: Save,
} );

نکته‌ای که در سال‌های اخیر باعث تغییرات زیادی شده: از apiVersion: 3 به بعد، در Metadata همان block.json می‌توان تنظیمات زیادی را تعریف کرد و دیگر نیازی به تکرارشان در registerBlockType نیست. این باعث می‌شود فایل index.js بسیار کوتاه‌تر و خواناتر باشد.

گام چهارم: تابع ویرایش (Edit)

تابع Edit، رابط ویرایشگر بلوک است. الگوی آن با کامپوننت‌های هسته وردپرس:

import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function Edit( { attributes, setAttributes } ) {
    const blockProps = useBlockProps();

    return (
        <div { ...blockProps }>
            <RichText
                tagName="h3"
                value={ attributes.title }
                onChange={ ( title ) => setAttributes( { title } ) }
                placeholder="عنوان کارت"
            />
            <RichText
                tagName="p"
                value={ attributes.content }
                onChange={ ( content ) => setAttributes( { content } ) }
                placeholder="محتوای کارت"
            />
        </div>
    );
}

سه نکته مهم در این کد: اول، useBlockProps() را همیشه استفاده کنید؛ این هوک، کلاس‌ها و attribute‌های لازم بلوک را اعمال می‌کند و اگر آن را نگذارید، بلوک در ویرایشگر ظاهر می‌شود ولی در فرانت‌اند مشکل خواهد داشت. دوم، RichText ابزار استاندارد وردپرس برای متن قابل‌ویرایش است؛ از آن به‌جای textarea یا input ساده استفاده کنید چون هم تجربه کاربری بهتری می‌دهد و هم در ذخیره‌سازی، HTML معتبرتری تولید می‌کند. سوم، پیش از دست‌زدن به هر بلوک، توصیه می‌کنم مقاله گوتنبرگ و آینده ویرایش محتوا در وردپرس را بخوانید تا فلسفه طراحی این لایه را درک کنید.

گام پنجم: تابع ذخیره (Save)

تابع Save، خروجی بلوک را در دیتابیس تولید می‌کند. مهم‌ترین قاعده در این تابع: خروجی باید دقیقاً با ساختاری که در تابع Edit استفاده کرده‌اید هم‌خوان باشد، وگرنه وردپرس پیام Block validation failed می‌دهد. الگوی Save برای بلوک ساده:

import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function Save( { attributes } ) {
    const blockProps = useBlockProps.save();

    return (
        <div { ...blockProps }>
            <RichText.Content
                tagName="h3"
                value={ attributes.title }
            />
            <RichText.Content
                tagName="p"
                value={ attributes.content }
            />
        </div>
    );
}

نکته‌ای که در چند پروژه بارها دیده‌ام: اگر تابع Edit را تغییر دهید ولی تابع Save را متناسب با آن به‌روز نکنید، همه بلوک‌های موجود در سایت شما پیام خطا می‌دهند. برای جلوگیری از این مشکل، پیش از انتشار بلوک، یا تابع Save را نهایی کنید و بعد دست به Edit نزنید، یا از بلوک پویا استفاده کنید که در بخش بعدی توضیح داده‌ام.

تابع Save در واقع تعهد شما به ساختار داده است. اگر این تعهد را بشکنید، حتی خودتان هم نمی‌توانید بلوک‌های قدیمی سایتتان را ویرایش کنید.

گام ششم: مدیریت attribute‌ها

attribute‌ها در بلوک گوتنبرگ، معادل پایگاه داده‌ای آن هستند. سه نکته مهم در مدیریت آن‌ها:

  1. نوع داده را دقیق تعیین کنید. در block.json برای هر attribute type مشخص کنید — string، number، boolean، array، object. اگر این را نادرست تنظیم کنید، بلوک در ذخیره‌سازی رفتار عجیبی نشان می‌دهد.
  2. مقدار پیش‌فرض بدهید. برای هر attribute یک default تعیین کنید تا کاربر در تجربه اول با بلوک خالی مواجه نشود.
  3. برای متن‌های غنی از source استفاده کنید. اگر مقدار attribute را مستقیم از HTML استخراج می‌کنید، از source: "html" و selector استفاده کنید:
"title": {
  "type": "string",
  "source": "html",
  "selector": "h3"
}

با این الگو، وردپرس خودش مقدار را از HTML ذخیره‌شده استخراج می‌کند و شما نیازی به ذخیره جداگانه در متادیتا ندارید. این رویکرد باعث می‌شود داده بلوک، در خروجی HTML قابل جستجو و در صورت مهاجرت، قابل انتقال باشد.

گام هفتم: استایل ویرایشگر و فرانت‌اند

در بلوک‌های گوتنبرگ، سه نوع استایل دارید و تفکیک آن‌ها در تجربه من مهم‌ترین نکته در ساخت بلوک‌های حرفه‌ای است:

  • style.scss: برای استایل مشترک ویرایشگر و فرانت‌اند. هر چیزی که ظاهر بلوک را می‌سازد این‌جا قرار می‌گیرد.
  • editor.scss: برای استایل‌هایی که فقط در ویرایشگر لازم است — مثل حاشیه یا پلیس‌هولدر. این استایل در فرانت‌اند بارگذاری نمی‌شود.
  • view.scss: برای استایل‌هایی که فقط در فرانت‌اند لازم است، مثل انیمیشن‌هایی که در ویرایشگر نباید اجرا شوند.

اگر از کلاس‌ها درست استفاده کنید، بلوک شما در ویرایشگر مثل پیش‌نمایش دقیق فرانت‌اند دیده می‌شود. توصیه‌ام این است که هر استایل را در قالب‌بندی مشترک بنویسید تا بین ویرایشگر و فرانت‌اند اختلاف ظاهری نباشد. مبانی کدنویسی استاندارد در وردپرس در استانداردهای کدنویسی وردپرس آمده است.

گام هشتم: بلوک پویا با ServerSideRender

گاهی بلوک شما باید محتوایش را از دیتابیس بخواند — مثل نمایش آخرین محصولات، آخرین نوشته‌های یک دسته، یا نمایش اطلاعات کاربر. در این حالت، تابع Save لازم نیست HTML را ذخیره کند چون خروجی در زمان اجرا محاسبه می‌شود. الگوی بلوک پویا:

<?php
function myplugin_render_info_card( $attributes ) {
    $title   = isset( $attributes['title'] ) ? $attributes['title'] : '';
    $content = isset( $attributes['content'] ) ? $attributes['content'] : '';

    return sprintf(
        '<div class="wp-block-myplugin-info-card"><h3>%s</h3><p>%s</p></div>',
        esc_html( $title ),
        esc_html( $content )
    );
}

register_block_type( __DIR__ . '/build/info-card', array(
    'render_callback' => 'myplugin_render_info_card',
) );

در تابع Save برای بلوک پویا، معمولاً یک رشته خالی برگردانید چون وردپرس از render_callback استفاده می‌کند. برای پیش‌نمایش در ویرایشگر، می‌توانید از کامپوننت ServerSideRender استفاده کنید، ولی در تجربه من برای بلوک‌های ساده، پیش‌نمایش سبک در سمت کلاینت هم کافی است. تفاوت بلوک ثابت و پویا در جدول زیر خلاصه شده است:

معیاربلوک ثابتبلوک پویا
ذخیره HTML در دیتابیسبلهخیر
تغییر ساختار در آیندهنیاز به مهاجرت دادهراحت
مناسب برایمحتوای ثابت کاربرداده پویا از دیتابیس
سرعت فرانت‌اندسریع‌تربستگی به کوئری دارد

اشتباهات رایج در ساخت بلوک

در پرونده‌های زیادی که در ساخت بلوک گوتنبرگ بررسی کرده‌ام، پنج اشتباه بیشتر از بقیه تکرار شده:

  • عدم تطابق Edit و Save. اگر ساختار HTML در Edit و Save یکی نباشد، بلوک در ویرایشگر خطای Block validation failed می‌دهد. مهم‌ترین قاعده: همیشه ساختار را یکی نگه دارید.
  • فراموش کردن useBlockProps. بدون این هوک، کلاس‌ها و attribute‌های بلوک به خروجی اضافه نمی‌شوند و در نتیجه استایل‌ها در فرانت‌اند اعمال نمی‌شود.
  • ثبت بلوک در هوک اشتباه. register_block_type باید در هوک init باشد. اگر آن را در after_setup_theme یا مستقیم در بالای فایل قرار دهید، بلوک در ویرایشگر دیده نمی‌شود.
  • نبود namespace یکتا در نام بلوک. نام بلوک باید شامل namespace/block-name باشد. اگر فقط info-card را به‌عنوان نام بگذارید، با بلوک‌های افزونه‌های دیگر تعارض پیدا می‌کند.
  • فراموش کردن تست فرانت‌اند. بعضی از خطاهای بلوک فقط در فرانت‌اند ظاهر می‌شوند. مثلاً اگر خروجی Save شامل تگ‌های اشتباه باشد، در ویرایشگر کار می‌کند ولی در فرانت‌اند به‌هم می‌ریزد.

اشتباه ششمی هم هست که کمتر گفته می‌شود ولی در پروژه‌های واقعی دیده‌ام: نوشتن کد بلوک در قالب به‌جای افزونه. اگر بلوک در قالب قرار بگیرد، روز تغییر قالب، بلوک شما از بین می‌رود و محتوای نوشته‌هایی که از آن استفاده کرده‌اند، به HTML خام تبدیل می‌شود. همیشه بلوک را در افزونه سفارشی قرار دهید؛ همان منطقی که در کدنویسی اختصاصی برای افزونه وردپرس روی آن تأکید کرده‌ام.

روش تست و دیباگ بلوک

چهار ابزار در تست و دیباگ بلوک گوتنبرگ همیشه در دسترس من هستند:

  1. کنسول مرورگر: بیشتر خطاهای بلوک در کنسول مرورگر ظاهر می‌شوند. اگر بلوک ثبت نمی‌شود، اولین جایی که باید نگاه کنید همین‌جاست.
  2. لاگ دیباگ وردپرس: خطاهای سمت سرور، مخصوصاً در بلوک‌های پویا، در فایل wp-content/debug.log ثبت می‌شوند. اگر با مفهوم این لاگ آشنا نیستید، پیدا کردن خطاهای جاوااسکریپت در کنسول مرورگر نقطه شروع خوبی است.
  3. ابزار Block Validation: گوتنبرگ خودش یک پیام دقیق درباره عدم تطابق Edit و Save می‌دهد. اگر این پیام را در ویرایشگر دیدید، همیشه از بخش Console مرورگر جزئیات را ببینید.
  4. محیط استجینگ: بلوک را اول در محیط استجینگ تست کنید، به‌خصوص اگر روی سایت زنده نصب می‌شود. مسیر استفاده از افزودن کد سفارشی به وردپرس در قالب چایلد هم برای تست مفید است.

یک عادت شخصی که از تجربه‌های تلخ شکل گرفته: پیش از هر تغییر در بلوکی که در سایت زنده استفاده شده، یک نسخه پشتیبان از پوشه افزونه بگیرم. اگر تغییر باعث خطای Block validation failed در همه بلوک‌های موجود سایت شود، بازگشت به نسخه قبلی در چند دقیقه حل می‌شود، در حالی که بدون بکاپ می‌تواند چند ساعت وقت بگیرد.

بلوک سفارشی و بهینه‌سازی AEO

از دید AEO (Answer Engine Optimization یا بهینه‌سازی برای موتورهای پاسخ‌ده)، بلوک سفارشی می‌تواند یک لایه مهم در ساختار محتوای شما باشد. موتورهای جستجوی امروز و به‌خصوص مدل‌های زبان بزرگ، به ساختار HTML و معنای قطعات محتوا نگاه می‌کنند. اگر بلوک شما بخشی مثل پرسش و پاسخ، جدول مقایسه، یا اطلاعات منظم را تولید می‌کند، می‌توانید در خروجی از تگ‌های معنایی و حتی داده‌ساختاریافته (Schema) استفاده کنید تا سایت شما شانس بیشتری برای نمایش در نتایج غنی داشته باشد.

یک مثال عملی که در پروژه‌های خودم به‌کار برده‌ام: بلوکی برای نمایش پرسش‌های متداول که خروجی HTML آن شامل <details><summary> است و به‌علاوه ساختار FAQPage را به‌عنوان Schema می‌سازد. نتیجه این بلوک، هم تجربه کاربری بهتری است و هم احتمال بیشتری برای ظهور در نتایج پرسش و پاسخ گوگل. اگر با مفهوم کلی سئوی تکنیکال و داده‌ساختاریافته آشنا نیستید، SEO تکنیکال: از خزش تا ایندکس لایه‌های زیرین را توضیح می‌دهد.

بلوک‌ها از دید معماری نرم‌افزار

برای مهندسانی که با معماری نرم‌افزار سروکار دارند، ارزش دارد بلوک گوتنبرگ را به‌عنوان یک کامپوننت خودمختار (Autonomous Component) نگاه کنند که سه مسئولیت مستقل دارد: حالت (State)، نمایش (Render) و ذخیره‌سازی (Persist). این تفکیک، همان الگوی کامپوننت‌های مدرن در React، Vue یا Svelte است که در آن کامپوننت‌ها جدا و قابل بازاستفاده طراحی می‌شوند.

سه مشاهده دقیق‌تر از تجربه‌های میدانی: اول، در پروژه‌های بزرگ با چند توسعه‌دهنده، مسئله نسخه‌بندی بلوک به یک چالش جدی تبدیل می‌شود. اگر تابع Save در نسخه دوم تغییر کند، بلوک‌هایی که با نسخه اول ساخته شده‌اند، در ویرایشگر خطا می‌دهند. راه‌حل در معماری مدرن، استفاده از مفهوم deprecated است که به وردپرس می‌گوید نسخه‌های قبلی چطور به نسخه جدید تبدیل شوند. اگر با مفهوم نسخه‌بندی API آشنا نیستید، استانداردهای کدنویسی وردپرس چارچوب کلی را می‌دهد.

دوم، در معماری Headless که فرانت‌اند جدا از وردپرس سرو می‌شود، بلوک گوتنبرگ به یک لایه میانی تبدیل می‌شود بین داده ذخیره‌شده و نمایش نهایی. اگر فرانت‌اند شما React یا Vue است، باید تصمیم بگیرید که آیا خروجی بلوک را در فرانت‌اند بازسازی می‌کنید یا آن را به‌صورت HTML آماده از REST API می‌گیرید. هر دو رویکرد مزایایی دارند، ولی استفاده از HTML آماده در تجربه من سریع‌تر و ساده‌تر است؛ به شرطی که بلوک شما در ساختار HTML خودش دقیق باشد.

سوم، در سیستم‌های چند-مستأجری (Multi-Tenant) که چند سایت روی یک نصب وردپرس سرو می‌شوند، بلوک‌ها می‌توانند یک منبع بزرگ برای اشتراک‌گذاری کد باشند. اگر یک بلوک را در یک سایت شبکه ثبت کنید، بقیه سایت‌ها می‌توانند همان بلوک را در محتوای خود استفاده کنند. این یعنی معماری درست، هزینه ساخت بلوک را بین چند سایت توزیع می‌کند و سرعت توسعه را بالا می‌برد. با این حال، به‌همین دلیل هم نیاز به مدیریت نسخه دقیق‌تر است، چون تغییری در بلوک، روی همه سایت‌ها اثر می‌گذارد.

چهارم، در CI/CD (Continuous Integration / Continuous Deployment یا یکپارچه‌سازی و استقرار پیوسته)، بلوک‌ها باید به‌عنوان بخشی از کدبیس مدیریت شوند. یعنی نه در پیشخوان ویرایش دستی، نه از طریق مدیریت فایل. بلوک در مخزن Git، نسخه‌بندی‌شده، با تست خودکار — این همان انضباطی است که پروژه‌های نرم‌افزاری مدرن دارند و در وردپرس کمتر دیده می‌شود. نتایج این رویکرد در پروژه‌های بزرگ، تفاوت بین پایداری و بحران ماهانه است.

پنجم، در معماری بلوک‌محور نسل جدید وردپرس (Block Themes)، خود قالب هم مجموعه‌ای از بلوک‌هاست. یعنی در آینده‌ای نه‌چندان دور، بلوک‌های سفارشی شما بخشی از هویت قالب خواهند بود، نه افزونه‌ای اضافی. این تغییر پارادایم، ساختار مدیریت و نگهداری بلوک‌ها را هم تغییر می‌دهد. اگر پروژه شما در افق چندساله دیده می‌شود، از امروز بلوک‌هایتان را با معیار قالب بلوکی طراحی کنید — یعنی ساختاری معنایی، قابل‌بازاستفاده و وابسته به هسته وردپرس، نه به صفحات ثابت.

خط بسته‌شدن یک بلوک

اگر بخواهم این مقاله را در سه نکته فشرده کنم: اول، ساخت بلوک گوتنبرگ یک پروژه کوچک است با ساختار مشخص — block.json، index، edit، save و استایل‌ها. هر فایل سر جای خودش، خطا کاهش پیدا می‌کند. دوم، همیشه بلوک را در افزونه سفارشی قرار دهید، نه در قالب؛ این کار بلوک شما را از تغییر قالب محفوظ می‌دارد. سوم، پیش از انتشار بلوک، تابع Save را نهایی کنید و از آن پس به آن دست نزنید؛ اگر نیاز به تغییر ساختار داشتید، از deprecated استفاده کنید تا بلوک‌های قبلی نشکنند.

پیشنهاد عملی من برای همین هفته: اگر تا حالا بلوک سفارشی نساخته‌اید، با npx @wordpress/create-block یک بلوک ساده بسازید و در محیط لوکال تست کنید. مسیر نصب و راه‌اندازی با توسعه وردپرس با محیط لوکال شروع می‌شود. اگر با ساختار افزونه آشنا نیستید، راهنمای توسعه افزونه از صفر پیش از بلوک‌نویسی توصیه می‌شود. همچنین برای درک بهتر تفاوت بلوک با ویجت، ساخت ویجت سفارشی با کدنویسی وردپرس تصویر کامل‌تری می‌دهد. اگر در پروژه‌ای با خطای بلوک گوتنبرگ مواجه شده‌اید که در این فهرست نبوده — به‌خصوص اگر در قالب‌های بلوکی یا معماری Headless بوده — برایم بنویسید کدام علت ریشه‌ای بود و چطور به جواب رسیدید. تجربه‌های واقعی شما همان چیزی است که این راهنما را برای نفر بعدی دقیق‌تر می‌کند. 🧱