title: >- Composición de Bloques (useInnerBlocksProps), Block Hooks API y Patrones Sincronizados con Overrides description: >- Arquitectura de contenedores compuestos y sistemas de diseño en WordPress 7.0+ (PHP 8.4+, TypeScript 5.8): fusión limpia del DOM mediante useInnerBlocksProps, bloqueo estructural templateLock ('contentOnly'), inyección automática de bloques vía Block Hooks API y patrones sincronizados con sobrescrituras de contenido. module: gutenberg-blocks order: 4 difficulty: advanced estimatedTime: 40 min topics:
Pretender construir un sitio web corporativo de envergadura sobre bloques atómicos monolíticos (donde una sección de «Tabla de Precios» acumula 45 atributos planos en block.json: plan1Title, plan1Price, plan1Feature1, plan2Title...) convierte rápidamente el mantenimiento en una pesadilla. En lugar de sobrecargar un solo bloque con decenas de campos, la arquitectura moderna de Gutenberg en WordPress 7.0+ («Armstrong») se sustenta en la Composición Jerárquica de Nodos (Compound Block Pattern).
Una sección compleja se descompone en un Contenedor Padre (agentic-lab/bento-grid) y Bloques Hijos Estrictamente Delimitados (agentic-lab/bento-cell). Con el estándar de 2026 han quedado erradicados tres problemas históricos de las primeras versiones de Gutenberg:
<InnerBlocks /> introducía envoltorios intermedios div.block-editor-inner-blocks > div.block-editor-block-list__layout entre el padre y los hijos, haciendo que las maquetaciones en CSS Grid y Flexbox colapsaran en la interfaz de administración. El hook moderno useInnerBlocksProps() fusiona las propiedades del contenedor (useBlockProps) y la lista de hijos directamente en una única etiqueta semántica limpia (<ul {...innerBlocksProps}>).templateLock: "contentOnly" y los Synced Pattern Overrides (core/pattern-overrides) permiten congelar la rejilla, los márgenes y la estructura visual de forma centralizada en todo el sitio, otorgando a los redactores acceso exclusivamente para modificar textos de cabecera, imágenes y enlaces.hooked_blocks) permite a un plugin «enganchar» declarativamente su bloque (por ejemplo, insignia de tiempo de lectura, biografía de autor o sello de verificación) inmediatamente después de core/post-title o dentro de core/navigation en todos los temas de bloques sin realizar consultas a la base de datos.Imagina la composición de bloques en WordPress 7.0+ como un chasis de rack para servidores modulares (Rack Chassis):
useInnerBlocksProps) establece el marco de soporte (CSS Grid), el suministro de energía (providesContext) y las reglas de admisión: en sus bahías solo encajan módulos de servidor certificados (allowedBlocks: ['agentic-lab/bento-cell']), y estos módulos no pueden operar sueltos fuera del chasis ("parent": ["agentic-lab/bento-grid"] en el block.json hijo).templateLock: "contentOnly") sella la puerta del armario: los operadores solo pueden manipular las pantallas de diagnóstico del frontal, sin posibilidad de extraer módulos ni cambiar su distribución.Block Hooks API) fija automáticamente una sonda de telemetría a cada nuevo bastidor en la cadena de ensamblaje en cuanto este se enciende.┌─────────────────────────────────────────────────────────────────────────┐
│ PADRE: agentic-lab/bento-grid (useInnerBlocksProps -> <div.grid>) │
│ • providesContext: { "agentic-lab/gridColumns": 3 } │
│ • allowedBlocks: ["agentic-lab/bento-cell"] │
│ • templateLock: "contentOnly" (o "insert" / false) │
│ │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ HIJO: agentic-lab/bento-cell │ │ HIJO: agentic/bento-cell │ │
│ │ • parent: ["agentic/bento-grid"]│ │ • usesContext: │ │
│ │ • ¡Hijo Directo de CSS Grid! │ │ ["agentic/gridColumns"] │ │
│ └─────────────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘El hook fusiona useBlockProps y el contenedor de hijos en un solo nodo DOM, garantizando 100% de paridad en CSS Grid entre el editor y el frontend.
El núcleo valida la jerarquía de inserción: el bloque hijo solo se muestra en el insertador cuando el foco reside en un ancestro autorizado.
El modo contentOnly oculta contenedores estructurales del árbol de capas, limitando la edición a los campos de texto y recursos multimedia designados.
Durante el ensamblaje de la plantilla, el núcleo inyecta bloques anclados antes/después de bloques clave (como inmediatamente tras core/post-content).
En los proyectos empresariales de Fullstack Agentic Lab, el ecosistema de useInnerBlocksProps, Block Hooks API y Synced Patterns se utiliza para:
useInnerBlocksProps, las tarjetas bento-cell son hijos directos de <div class="agentic-bento-grid"> tanto en el editor como en el frontend, posibilitando grid-column: span 2 sin distorsiones en el DOM de Gutenberg.Block Hooks API): vinculación del bloque de «Tiempo de lectura y Resumen IA» justo después de core/post-title en todas las plantillas single.html con una simple declaración en block.json y un filtro en PHP 8.4+.register_block_pattern) con templateLock: "contentOnly" y Synced Pattern Overrides.Comparativa entre el componente heredado <InnerBlocks /> y el estándar moderno WordPress 7.0+:
A continuación se muestra un artefacto de producción integral para WordPress 7.0+ (TypeScript 5.8 + PHP 8.4+). La primera parte implementa un contenedor Bento Grid con useInnerBlocksProps(), orientación horizontal y bloqueo contentOnly. La segunda parte implementa el inyector con Block Hooks API y el registro de un patrón empresarial con Pattern Overrides (core/pattern-overrides).
/**
* PARTE 1: Contenedor Bento Grid basado en useInnerBlocksProps (TypeScript 5.8)
* Archivo: 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';
}
// Restringir la inserción de hijos exclusivamente a celdas Bento Cell
const ALLOWED_CHILD_BLOCKS = ['agentic-lab/bento-cell'];
// Plantilla inicial de 3 celdas al insertar la rejilla en la página
const DEFAULT_BENTO_TEMPLATE: TemplateArray = [
[
'agentic-lab/bento-cell',
{
spanColumns: 2,
badge: 'WASM RUNTIME',
heading: 'PHP 8.4 ejecutado directamente en el navegador',
summary: 'Entorno de ejecución de WordPress aislado con 0ms de latencia de servidor.',
},
],
[
'agentic-lab/bento-cell',
{
spanColumns: 1,
badge: 'DUAL-LLM GATEWAY',
heading: 'Gemini 2.5 + Claude 3.5',
summary: 'Validación estricta de contratos Zod en respuestas en streaming.',
},
],
[
'agentic-lab/bento-cell',
{
spanColumns: 3,
badge: 'EDGE ISR',
heading: 'Invalidación instantánea de caché con Webhooks HMAC-SHA256',
summary: 'Latencia P95 inferior a 18ms en la red de distribución global.',
},
],
];
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. Configurar propiedades del nodo CSS Grid contenedor
const blockProps = useBlockProps({
className: `agentic-bento-grid has-${columns}-columns`,
style: {
display: 'grid',
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
gap: '1.5rem',
},
});
// 2. Fusionar InnerBlocks directamente en el <section> sin envoltorios div adicionales
const innerBlocksProps = useInnerBlocksProps(blockProps, {
allowedBlocks: ALLOWED_CHILD_BLOCKS,
template: DEFAULT_BENTO_TEMPLATE,
templateLock: lockMode === '' ? false : lockMode,
orientation: 'horizontal', // Controles de desplazamiento horizontal para grids
});
return (
<>
<InspectorControls>
<PanelBody title={__('Arquitectura de Rejilla Bento', 'agentic-lab')} initialOpen={true}>
<RangeControl
label={__('Número de Columnas Grid', 'agentic-lab')}
value={columns}
min={1}
max={4}
onChange={(val) => setAttributes({ columns: val ?? 3 })}
/>
<SelectControl
label={__('Modo de Bloqueo de Estructura (templateLock)', 'agentic-lab')}
value={lockMode}
options={[
{ label: 'Content Only (Solo Contenido — Recomendado)', value: 'contentOnly' },
{ label: 'Lock Insert (Permitir solo reordenación)', value: 'insert' },
{ label: 'Lock All (Bloqueo estructural total)', value: 'all' },
{ label: 'Unlocked (Libertad total de maquetación)', value: '' },
]}
onChange={(val) =>
setAttributes({ lockMode: val as BentoGridAttributes['lockMode'] })
}
/>
</PanelBody>
</InspectorControls>
{/* <section> semántico cuyos hijos DIRECTOS son las celdas 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',
},
});
// Serialización limpia de bloques hijos dentro del <section>
const innerBlocksProps = useInnerBlocksProps.save(blockProps);
return <section {...innerBlocksProps} />;
},
});
/*
* ============================================================================
* PARTE 2: WordPress 7.0+ (PHP 8.4+) — Block Hooks API & Synced Pattern Overrides
* Archivo: 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. Inyectar automáticamente el bloque de telemetría tras el título en todas las plantillas FSE
* add_filter('hooked_block_types', $this->injectTelemetryBadgeAfterTitle(...), 10, 4);
*
* // 2. Personalizar los atributos del bloque inyectado automáticamente
* add_filter('hooked_block_agentic-lab/telemetry-card', $this->configureHookedBlock(...), 10, 5);
*
* // 3. Registrar el patrón corporativo con soporte para 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 con Ranuras Sobrescribibles',
* '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">Arquitectura de Referencia del Sistema</h2>
* <!-- /wp:heading -->
* <!-- wp:paragraph {"metadata":{"name":"Hero Summary","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
* <p>Descripción de la solución técnica con sincronización de diseño centralizada.</p>
* <!-- /wp:paragraph -->
* </div>
* <!-- /wp:group -->',
* ]);
* }
* }
*/
En el block.json del padre declara providesContext, y en el del hijo declara parent: ['namespace/container'] y usesContext.
Combina useBlockProps y useInnerBlocksProps en una única etiqueta semántica indicando allowedBlocks, template y orientation.
Habilita el modo contentOnly o los Pattern Overrides (metadata.bindings.content -> core/pattern-overrides) para una entrega segura a redacción.
Si el componente debe integrarse en todas las plantillas del tema automáticamente, regístralo vía hooked_blocks en block.json.