Отметим: когда стандартные блоки Gutenberg не покрывают потребности проекта — например, нужна карточка товара с данными из кастомного типа записи или сложный контейнер с вложенными блоками — разработчики вынуждены искать альтернативы. В таких случаях мы создаём кастомные блоки на React, которые дают полный контроль над структурой, атрибутами и стилями. Наш опыт (более 5 лет, 50+ блоков) показывает: правильно спроектированный блок экономит часы работы редактора и ускоряет загрузку страницы за счёт оптимизированного кода.
В одном из проектов для агентства недвижимости потребовался блок «Карточка объекта» с данными из кастомного типа записи, поддержкой геолокации и галереи. Редактор выбирает объект из списка, а на фронте рендерится карточка с ценой, адресом и меткой на карте. Разработка на React сократила время загрузки редактора на 30%, а PHP-рендер обрабатывает запрос за 50ms.
Почему кастомные блоки Gutenberg лучше стандартных?
Стандартные блоки ограничены базовым функционалом. Кастомные блоки дают полный контроль над структурой, атрибутами и стилями. Вы можете добавить панель настроек с любыми полями, связать блок с произвольными типами записей и реализовать уникальную логику. По сравнению с ACF-блоками, кастомные на React обеспечивают на 40% более гибкий интерфейс редактора и не уступают по производительности.
Создание динамического блока Gutenberg: пошаговый план
Разработка блока делится на этапы:
- Аналитика — выявление требований к блоку и его атрибутам.
- Проектирование — определение структуры, связей с данными и поддержки тем.
- Регистрация через
block.json— описание метаданных, атрибутов и скриптов. - JavaScript-компоненты — создание
edit(панель настроек) иsave(статический или() => nullдля динамического). - PHP-рендер — написание
render_callbackдля динамических блоков. - Сборка через
@wordpress/scripts— автоматическая компиляция JS и CSS. - Тестирование — проверка в разных браузерах и окружениях.
Типовой блок средней сложности занимает 1-3 дня. Рассмотрим ключевые шаги.
Регистрация блока
Согласно официальной документации, block.json является стандартом регистрации блоков. WordPress Developer Resources
Современный подход — block.json + JavaScript/PHP:
{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "my-plugin/project-card", "version": "1.0.0", "title": "Карточка проекта", "category": "common", "icon": "portfolio", "description": "Выводит карточку портфолио-проекта с изображением и описанием", "supports": { "html": false, "align": ["wide", "full"], "color": { "background": true, "text": true } }, "attributes": { "projectId": { "type": "number" }, "showDescription": { "type": "boolean", "default": true }, "imageSize": { "type": "string", "default": "large" } }, "editorScript": "file:./index.js", "editorStyle": "file:./editor.css", "style": "file:./style.css" } Регистрация в PHP:
add_action('init', function () { register_block_type(__DIR__ . '/blocks/project-card'); }); JavaScript: edit и save
import { registerBlockType } from '@wordpress/blocks'; import { useBlockProps, InspectorControls, MediaUpload } from '@wordpress/block-editor'; import { PanelBody, ToggleControl, SelectControl, Button } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; registerBlockType('my-plugin/project-card', { edit: ({ attributes, setAttributes }) => { const { projectId, showDescription, imageSize } = attributes; const blockProps = useBlockProps({ className: 'project-card-editor' }); const projects = useSelect(select => select('core').getEntityRecords('postType', 'project', { per_page: 50 }) ); const projectOptions = projects ? [{ label: '— выберите проект —', value: 0 }, ...projects.map(p => ({ label: p.title.rendered, value: p.id }))] : [{ label: 'Загрузка...', value: 0 }]; return ( <> <InspectorControls> <PanelBody title="Настройки блока"> <SelectControl label="Проект" value={projectId} options={projectOptions} onChange={v => setAttributes({ projectId: Number(v) })} /> <ToggleControl label="Показывать описание" checked={showDescription} onChange={v => setAttributes({ showDescription: v })} /> <SelectControl label="Размер изображения" value={imageSize} options={[ { label: 'Thumbnail', value: 'thumbnail' }, { label: 'Medium', value: 'medium' }, { label: 'Large', value: 'large' }, ]} onChange={v => setAttributes({ imageSize: v })} /> </PanelBody> </InspectorControls> <div {...blockProps}> {projectId ? <ProjectCardPreview projectId={projectId} showDescription={showDescription} /> : <p>Выберите проект в панели справа</p> } </div> </> ); }, save: () => null, // динамический блок — рендер через PHP }); save: () => null означает, что блок динамический — контент рендерится PHP в момент запроса страницы. Это предпочтительно для блоков, данные которых меняются (записи из БД).
PHP-рендеринг динамического блока
register_block_type(__DIR__ . '/blocks/project-card', [ 'render_callback' => 'my_plugin_render_project_card', ]); function my_plugin_render_project_card(array $attributes): string { $project_id = absint($attributes['projectId'] ?? 0); $show_desc = (bool) ($attributes['showDescription'] ?? true); $image_size = sanitize_key($attributes['imageSize'] ?? 'large'); if (!$project_id) return ''; $project = get_post($project_id); if (!$project || $project->post_status !== 'publish') return ''; $thumbnail = get_the_post_thumbnail($project_id, $image_size, ['class' => 'project-card__image']); $title = esc_html($project->post_title); $permalink = esc_url(get_permalink($project_id)); $excerpt = $show_desc ? '<p class="project-card__desc">' . esc_html(get_the_excerpt($project)) . '</p>' : ''; $wrapper_attributes = get_block_wrapper_attributes(['class' => 'project-card']); return "<article {$wrapper_attributes}> {$thumbnail} <h3 class=\"project-card__title\"><a href=\"{$permalink}\">{$title}</a></h3> {$excerpt} </article>"; } get_block_wrapper_attributes() добавляет классы из supports.color и другие атрибуты, которые Gutenberg генерирует автоматически.
Блок с innerBlocks
Блоки-контейнеры принимают дочерние блоки через InnerBlocks:
import { InnerBlocks } from '@wordpress/block-editor'; const ALLOWED_BLOCKS = ['core/paragraph', 'core/heading', 'my-plugin/cta-button']; const TEMPLATE = [ ['core/heading', { level: 3, placeholder: 'Заголовок секции' }], ['core/paragraph', { placeholder: 'Описание...' }], ['my-plugin/cta-button', {}], ]; // В edit: <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} template={TEMPLATE} templateLock={false} /> // В save: <InnerBlocks.Content /> Сравнение типов блоков
| Тип блока | Сохранение | Производительность | Гибкость | Когда использовать |
|---|---|---|---|---|
| Статический | HTML в записи | Высокая (нет серверного рендера) | Низкая (фиксированный HTML) | Контент не меняется, простые компоненты |
| Динамический | Только атрибуты | Средняя (рендер на каждый запрос) | Высокая (можно менять логику) | Данные из БД, сложные вычисления |
| Контейнер (innerBlocks) | Сохраняет вложенные блоки | Зависит от вложенности | Очень высокая | Секции с произвольным наполнением |
Что входит в разработку блока под ключ
При заказе кастомных блоков у нас вы получаете:
- Файл
block.jsonс полным описанием атрибутов и поддержкой цветов/выравнивания. - JavaScript-компоненты edit (с панелью InspectorControls) и save (для статических) или
() => null(для динамических). - PHP-рендер с использованием
get_block_wrapper_attributes()и защитой данных. - CSS-стили (editor и frontend) с БЭМ-неймингом.
- Документацию по использованию блока для редакторов.
- Обучение редактора или команды (по запросу).
- Гарантию на код и поддержку в течение 1 месяца после сдачи.
Типовые сроки разработки
| Тип блока | Сроки |
|---|---|
| Простой статический (2-3 атрибута) | 4-8 часов |
| Динамический с PHP-рендером | 1-2 дня |
| Блок-контейнер с innerBlocks | 2-4 дня |
| Набор 5-10 блоков для дизайн-системы | от 2 недель |
Точные сроки зависят от сложности — свяжитесь с нами для оценки. Мы разрабатываем блоки под ключ с гарантией совместимости с последней версией WordPress.
Как использовать dynamic blocks для работы с данными из БД?
Dynamic blocks идеальны для вывода контента из кастомных типов записей. Достаточно сохранить ID записи в атрибутах, а PHP-рендер подтягивает данные в момент генерации страницы. Это позволяет менять содержимое без пересохранения постов — достаточно обновить исходную запись. Например, блок «Список новостей» с фильтрацией по категориям: редактор выбирает категорию, а на сайте выводятся последние 10 новостей с пагинацией.
Пример сложного блока с кастомными полями
Для создания блока с кастомными полями, такими как повторитель или группа полей, используйте `RichText` и `PanelBody`. Например, блок "Команда" с повторяемыми карточками сотрудников: фото, имя, должность. Код включает `useSelect` для получения медиафайлов и `InnerBlocks` для описания. Средняя экономия времени редактора — 20%, а код блока занимает 200-400 строк.Наши преимущества
Более 5 лет опыта, 50+ проектов, сертифицированные специалисты. Мы используем React, TypeScript, CSS Modules, CI/CD. Каждый блок проходит код-ревью и тестирование в разных окружениях. Наши клиенты получают не просто код, а готовое решение с документацией и поддержкой. Закажите разработку кастомных блоков для вашего проекта — получите консультацию бесплатно. Свяжитесь с нами для обсуждения.







