Відзначимо: коли стандартні блоки 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. Кожен блок проходить код-рев'ю та тестування в різних оточеннях. Наші клієнти отримують не просто код, а готове рішення з документацією та підтримкою. Замовте розробку кастомних блоків для вашого проекту — отримайте консультацію безкоштовно. Зв'яжіться з нами для обговорення.







