title: >- Композиція блоків (useInnerBlocksProps), Block Hooks API та Synced Patterns з Overrides description: >- Архітектура складених контейнерів і дизайн-систем у WordPress 7.0+ (PHP 8.4+, TypeScript 5.8): безшовне злиття DOM через useInnerBlocksProps, блокування структури templateLock ('contentOnly'), автоматичне впровадження блоків через Block Hooks API та синхронні патерни з перевизначенням контенту. module: gutenberg-blocks order: 4 difficulty: advanced estimatedTime: 40 хв topics:
Спроба побудувати великий корпоративний сайт на атомарних монолітних блоках (де в однієї секції «Тарифна сітка» є 45 пласких атрибутів у block.json: plan1Title, plan1Price, plan1Feature1, plan2Title...) швидко перетворює підтримку на кошмар. Замість перевантаження одного блока десятками полів сучасна архітектура Gutenberg у WordPress 7.0+ («Armstrong») спирається на Ієрархічну Композицію Вузлів (Compound Block Pattern).
Складна секція розбивається на Батьківський Контейнер (agentic-lab/bento-grid) та Суворо Обмежені Дочірні Блоки (agentic-lab/bento-cell). При цьому в стандарті 2026 року залишилися в минулому три головні вади раннього Gutenberg:
div-обгортки в редакторі (Wrapper Div Soup): старий компонент <InnerBlocks /> вставляв між батьком і дітьми службові div.block-editor-inner-blocks > div.block-editor-block-list__layout, через що верстка на CSS Grid та Flexbox в адмінці розвалювалася й вимагала костильних селекторів. Сучасний хук useInnerBlocksProps() зливає властивості контейнера (useBlockProps) та список дітей безпосередньо в один чистий семантичний тег (<ul {...innerBlocksProps}>).templateLock: "contentOnly" та Synced Pattern Overrides (core/pattern-overrides) дозволяють глобально зафіксувати сітку, відступи та дизайн секції в одному місці, залишивши контент-менеджерам доступ виключно до редагування тексту заголовків, зображень і посилань.hooked_blocks) дозволяє плагіну декларативно «пристебнути» свій блок (наприклад, кнопку лайка, блок автора чи бейдж верифікації) відразу після core/post-title або всередину core/navigation у всіх блокових темах без єдиної правки бази даних.Уявіть композицію блоків у WordPress 7.0+ як модульну серверну стійку (Rack Chassis):
useInnerBlocksProps) задає силову раму (CSS Grid), живлення (providesContext) та правила допуску: в його слоти фізично входять лише сертифіковані леза (allowedBlocks: ['agentic-lab/bento-cell']), а самі леза не можуть валятися на підлозі поза шасі ("parent": ["agentic-lab/bento-grid"] у дочірньому block.json).templateLock: "contentOnly") замикає корпус на ключ: оператор бачить тільки передню панель із текстовими екранами, але не може висмикнути модуль чи змінити їхній порядок.Block Hooks API) сам підключає датчик телеметрії до кожного нового шасі на заводі, щойно те сходить із конвеєра.┌─────────────────────────────────────────────────────────────────────────┐
│ БАТЬКО: agentic-lab/bento-grid (useInnerBlocksProps -> <div.grid>) │
│ • providesContext: { "agentic-lab/gridColumns": 3 } │
│ • allowedBlocks: ["agentic-lab/bento-cell"] │
│ • templateLock: "contentOnly" (або "insert" / false) │
│ │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ ДИТИНА: agentic-lab/bento-cell │ │ ДИТИНА: agentic/bento-cell │ │
│ │ • parent: ["agentic/bento-grid"]│ │ • usesContext: │ │
│ │ • Прямий нащадок CSS Grid! │ │ ["agentic/gridColumns"] │ │
│ └─────────────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘Хук об'єднує useBlockProps і контейнер дочірніх блоків в один DOM-вузол, забезпечуючи 100% ідентичність CSS Grid у редакторі та на фронтенді.
Ядро валідує ієрархію вставки: дочірній вузол видно в інсертері тільки тоді, коли фокус знаходиться всередині дозволеного предка.
Режим contentOnly приховує технічні блоки-обгортки з дерева шарів і дозволяє редагувати лише позначені текстові та медіа-вузли.
На етапі рендерингу шаблону ядро автоматично впроваджує хук-блоки до/після цільових якірних блоків (наприклад, після core/post-content).
В інженерній практиці Fullstack Agentic Lab зв'язка useInnerBlocksProps, Block Hooks API та Synced Patterns застосовується для:
useInnerBlocksProps картки bento-cell рендеряться як прямі нащадки <div class="agentic-bento-grid"> і в редакторі, і на фронтенді, дозволяючи використовувати grid-column: span 2 без зламу внутрішньої розмітки Gutenberg.Block Hooks API): підключення блока «Час читання та AI-резюме статті» суворо після core/post-title у всіх шаблонах single.html однією декларацією у block.json та фільтром у PHP 8.4+.register_block_pattern) із режимом templateLock: "contentOnly" та Synced Pattern Overrides.Порівняємо застарілу реалізацію вкладених блоків через <InnerBlocks /> та сучасну архітектуру WordPress 7.0+:
Нижче наведено виробничий артефакт для WordPress 7.0+ (TypeScript 5.8 + PHP 8.4+). У першій частині реалізовано контейнер Bento Grid на базі useInnerBlocksProps() з горизонтальною орієнтацією та блокуванням contentOnly. У другій частині реалізовано серверний реєстратор Block Hooks API та корпоративного патерну з підтримкою Pattern Overrides (core/pattern-overrides).
/**
* ЧАСТИНА 1: Контейнер Bento Grid на базі useInnerBlocksProps (TypeScript 5.8)
* Файл: src/blocks/bento-grid/index.tsx
*/
import { registerBlockType, type TemplateArray } from '@wordpress/blocks';
import {
useBlockProps,
useInnerBlocksProps,
InspectorControls,
} from '@wordpress/block-editor';
import { PanelBody, RangeControl, SelectControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
interface BentoGridAttributes {
columns: number;
lockMode: '' | 'all' | 'insert' | 'contentOnly';
}
// Дозволяємо вставляти всередину сітки ТІЛЬКИ спеціалізовані комірки Bento Cell
const ALLOWED_CHILD_BLOCKS = ['agentic-lab/bento-cell'];
// Стартовий шаблон із 3 комірок під час додавання сітки на сторінку
const DEFAULT_BENTO_TEMPLATE: TemplateArray = [
[
'agentic-lab/bento-cell',
{
spanColumns: 2,
badge: 'WASM RUNTIME',
heading: 'PHP 8.4 безпосередньо в браузері',
summary: 'Ізольоване виконання WordPress без серверного RTT.',
},
],
[
'agentic-lab/bento-cell',
{
spanColumns: 1,
badge: 'DUAL-LLM GATEWAY',
heading: 'Gemini 2.5 + Claude 3.5',
summary: 'Сувора валідація відповідей за контрактами Zod.',
},
],
[
'agentic-lab/bento-cell',
{
spanColumns: 3,
badge: 'EDGE ISR',
heading: 'Миттєва інвалідація кешу через HMAC-SHA256 Webhooks',
summary: 'Час відгуку P95 < 18ms на глобальній мережі доставки.',
},
],
];
registerBlockType<BentoGridAttributes>('agentic-lab/bento-grid', {
apiVersion: 3,
title: __('Agentic Bento Architecture Grid', 'agentic-lab'),
category: 'agentic-lab-architecture',
icon: 'grid-view',
edit({ attributes, setAttributes }) {
const { columns, lockMode } = attributes;
// 1. Формуємо базові пропси батьківського CSS-Grid вузла
const blockProps = useBlockProps({
className: `agentic-bento-grid has-${columns}-columns`,
style: {
display: 'grid',
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
gap: '1.5rem',
},
});
// 2. Зливаємо InnerBlocks безпосередньо в наш <section> без єдиного зайвого <div>!
const innerBlocksProps = useInnerBlocksProps(blockProps, {
allowedBlocks: ALLOWED_CHILD_BLOCKS,
template: DEFAULT_BENTO_TEMPLATE,
templateLock: lockMode === '' ? false : lockMode,
orientation: 'horizontal', // Горизонтальні стрілки переміщення для гріда
});
return (
<>
<InspectorControls>
<PanelBody title={__('Архітектура Bento-сітки', 'agentic-lab')} initialOpen={true}>
<RangeControl
label={__('Кількість колонок Grid', 'agentic-lab')}
value={columns}
min={1}
max={4}
onChange={(val) => setAttributes({ columns: val ?? 3 })}
/>
<SelectControl
label={__('Режим захисту макета (templateLock)', 'agentic-lab')}
value={lockMode}
options={[
{ label: 'Content Only (Тільки тексти — Рекомендується)', value: 'contentOnly' },
{ label: 'Lock Insert (Дозволити тільки зміну порядку)', value: 'insert' },
{ label: 'Lock All (Повне блокування структури)', value: 'all' },
{ label: 'Unlocked (Повна свобода для архітектора)', value: '' },
]}
onChange={(val) =>
setAttributes({ lockMode: val as BentoGridAttributes['lockMode'] })
}
/>
</PanelBody>
</InspectorControls>
{/* Семантичний <section>, чиїми ПРЯМИМИ дітьми є комірки bento-cell */}
<section {...innerBlocksProps} />
</>
);
},
save({ attributes }) {
const { columns } = attributes;
const blockProps = useBlockProps.save({
className: `agentic-bento-grid has-${columns}-columns`,
style: {
display: 'grid',
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
gap: '1.5rem',
},
});
// Безшовна серіалізація дочірніх блоків усередину <section>
const innerBlocksProps = useInnerBlocksProps.save(blockProps);
return <section {...innerBlocksProps} />;
},
});
/*
* ============================================================================
* ЧАСТИНА 2: WordPress 7.0+ (PHP 8.4+) — Block Hooks API & Synced Pattern Overrides
* Файл: src/Blocks/BlockHooksAndPatternsOrchestrator.php
* ============================================================================
*
* <?php
* declare(strict_types=1);
*
* namespace AgenticLab\Blocks;
*
* use WP_Block_Template;
*
* final readonly class BlockHooksAndPatternsOrchestrator
* {
* public function registerHooks(): void
* {
* // 1. Автоматично пристібаємо блок телеметрії відразу після заголовка статті у всіх шаблонах FSE
* add_filter('hooked_block_types', $this->injectTelemetryBadgeAfterTitle(...), 10, 4);
*
* // 2. Налаштовуємо атрибути автоматично вставленого хук-блока
* add_filter('hooked_block_agentic-lab/telemetry-card', $this->configureHookedBlock(...), 10, 5);
*
* // 3. Реєструємо корпоративний патерн із підтримкою Pattern Overrides (core/pattern-overrides)
* add_action('init', $this->registerEnterprisePatterns(...));
* }
*
* public function injectTelemetryBadgeAfterTitle(
* array $hookedBlocks,
* string $position,
* string $anchorBlock,
* WP_Block_Template|array $context
* ): array {
* if ($anchorBlock === 'core/post-title' && $position === 'after') {
* $hookedBlocks[] = 'agentic-lab/telemetry-card';
* }
* return $hookedBlocks;
* }
*
* public function configureHookedBlock(
* ?array $parsedHookedBlock,
* string $hookedBlockType,
* string $relativePosition,
* array $parsedAnchorBlock,
* WP_Block_Template|array $context
* ): ?array {
* if ($parsedHookedBlock === null) {
* return null;
* }
* $parsedHookedBlock['attrs']['statusLevel'] = 'optimal';
* return $parsedHookedBlock;
* }
*
* public function registerEnterprisePatterns(): void
* {
* register_block_pattern('agentic-lab/verified-architecture-hero', [
* 'title' => 'Fullstack Agentic Lab — Hero з перевизначуваними слотами',
* 'categories' => ['featured', 'banner'],
* 'content' => '<!-- wp:group {"templateLock":"contentOnly","layout":{"type":"constrained"}} -->
* <div class="wp-block-group">
* <!-- wp:heading {"metadata":{"name":"Hero Title","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
* <h2 class="wp-block-heading">Еталонна архітектура системи</h2>
* <!-- /wp:heading -->
* <!-- wp:paragraph {"metadata":{"name":"Hero Summary","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
* <p>Опис інженерного рішення із синхронізацією дизайну по всьому сайту.</p>
* <!-- /wp:paragraph -->
* </div>
* <!-- /wp:group -->',
* ]);
* }
* }
*/
У block.json контейнера оголосіть providesContext, а в block.json дочірньої комірки — parent: ['namespace/container'] та usesContext.
Об'єднайте useBlockProps та useInnerBlocksProps на одному семантичному тезі із зазначенням allowedBlocks, template та orientation.
Активуйте режим contentOnly або Pattern Overrides (metadata.bindings.content -> core/pattern-overrides) для безпечної передачі контент-команді.
Якщо компонент повинен бути присутнім у всіх шаблонах блокової теми автоматично, зареєструйте його через hooked_blocks у block.json.