title: >- block.json Anatomy (Block API v3), Metadata Collections, and Modern Block Architecture description: >- In-depth analysis of the block.json specification in WordPress 7.0+ "Armstrong" (Block API v3): isolated iframe canvas editor, frontend ES modules (viewScriptModule), context tree (providesContext / usesContext), wp_register_block_metadata_collection() manifest compilation, and conditional asset loading. module: gutenberg-blocks order: 1 difficulty: intermediate estimatedTime: 35 min topics:
In early versions of Gutenberg, block registration was fractured across two disparate worlds: part of the configuration was duplicated in JavaScript inside registerBlockType(), while another part lived in PHP. This led to attribute schema desynchronization during server-side rendering (SSR), impossible static dependency analysis without running the full JS bundle, and mandatory downloads of hundreds of kilobytes of CSS and JS on every single page of the site.
In WordPress 7.0+ ("Armstrong"), the Single Source of Truth for any block is the declarative manifest block.json conforming to the Block API v3 specification ("apiVersion": 3). This JSON manifest simultaneously orchestrates four core subsystems:
apiVersion: 3): guarantees that administrative WordPress styles (wp-admin) never leak into your block's layout, ensuring accurate scaling across tablet and smartphone preview viewports.should_load_separate_core_block_assets): the core parses post_content prior to sending the HTTP response and injects onto the frontend (style, viewStyle, viewScriptModule) strictly those CSS/JS modules whose blocks actually exist on the current page.providesContext / usesContext): enables parent blocks to pass state to deeply nested descendant blocks without global variables or prop-drilling.wp_register_block_metadata_collection): eliminates disk I/O (file_get_contents + json_decode) when registering dozens of custom blocks in enterprise design systems.Think of block.json as a container shipping manifest in an automated container port (WordPress 7.0+ Core). The crane does not need to crack open the container and run the engine inside (execute your React bundle index.js) just to inspect its dimensions, gross weight, temperature regime, and destination terminal. By reading only the declarative tag block.json, the server immediately knows:
<!-- wp:agentic/metric-card {...} -->;theme.json v3) must be auto-generated in the editor's right sidebar via the supports block;viewScriptModule) must be enqueued via <script type="module"> in the visitor's browser.UNIFIED MANIFEST: block.json (apiVersion: 3)
│
┌────────────────────────────────┼────────────────────────────────┐
▼ ▼ ▼
┌───────────────────────┐ ┌───────────────────────┐ ┌───────────────────────┐
│ PHP REALM (Server) │ │ EDITOR REALM (Iframe) │ │ FRONTEND REALM (Edge) │
├───────────────────────┤ ├───────────────────────┤ ├───────────────────────┤
│ • Attribute Validation│ │ • editorScript (React)│ │ • style / viewStyle │
│ • render: render.php │ │ • editorStyle │ │ • viewScriptModule │
│ • Passing $context │ │ • Auto-generate │ │ (Native ES Modules) │
│ • Conditional Enqueue │ │ Inspector Controls │ │ • Interactivity API │
└───────────────────────┘ └───────────────────────┘ └───────────────────────┘The bundler compiles TypeScript/SCSS into build/, outputs .asset.php version hashes, and aggregates a single blocks-manifest.php for the entire library.
PHP registers the collection via wp_register_block_metadata_collection() and boots blocks from memory without querying dozens of JSON files on disk.
In Gutenberg, the block mounts inside an isolated <iframe> canvas; the core automatically constructs color, spacing, and border UI panels based on the supports declaration.
On post save, attributes are validated against the schema and serialized into post_content as a structured delimiter comment <!-- wp:namespace/slug -->.
During page render, the core detects the block, executes render.php, and loads viewScriptModule via native browser Import Maps ONLY on pages where the block appears.
Across Fullstack Agentic Lab enterprise projects, properly engineered block.json manifests fulfill four critical objectives:
editorScript (heavy authoring React dependencies) from lightweight viewScriptModule (< 3 KB), site visitors receive 0 bytes of editor overhead.supports): rather than implementing hundreds of lines of custom ColorPalette, BoxControl, and FontSizePicker components in React, declaring "spacing", "color", "typography", and "shadow" in block.json provides native Gutenberg UI controls synced with theme.json v3 tokens for free."supports": { "interactivity": true } unlocks client-side hydration of data-wp-* directives and seamless client navigation without page reloads.wp_register_block_types_from_metadata_collection() invocation with zero filesystem overhead.Comparing legacy block registration (Block API v1/v2 with manual wp_enqueue_script) against the modern Block API v3 in WordPress 7.0+:
Below is the production-ready reference artifact for WordPress 7.0+ and PHP 8.4+, featuring a complete block.json (apiVersion: 3) specification for an interactive infrastructure telemetry card and an enterprise collection loader with zero disk I/O.
<?php
/**
* Plugin Name: Fullstack Agentic Lab — Block API v3 & Metadata Collection Engine
* Description: Instant block library registration via precompiled manifest.
* Version: 3.2.0
* Requires at least: 6.8
* Requires PHP: 8.4
* Author: Artur Yusupov (Fullstack Agentic Lab)
*/
declare(strict_types=1);
namespace AgenticLab\Blocks;
defined('ABSPATH') || exit;
final readonly class BlockLibraryRegistrar
{
public function __construct(
private string $buildDir,
private string $manifestFile,
) {}
public function registerHooks(): void
{
add_action('init', $this->registerAllBlocks(...), 10);
add_filter('block_categories_all', $this->registerCustomCategory(...), 10, 1);
}
/**
* Registers the entire block collection in O(1) disk lookups (WordPress 6.8 / 7.0+).
*/
public function registerAllBlocks(): void
{
// Enforce separated asset loading strictly for blocks rendered on the current page
add_filter('should_load_separate_core_block_assets', '__return_true');
if (function_exists('wp_register_block_types_from_metadata_collection')) {
wp_register_block_types_from_metadata_collection($this->buildDir, $this->manifestFile);
return;
}
// Fallback for legacy runtimes with collection registration and manual loop
if (function_exists('wp_register_block_metadata_collection') && is_readable($this->manifestFile)) {
wp_register_block_metadata_collection($this->buildDir, $this->manifestFile);
/** @var array<string, array<string, mixed>> $manifest */
$manifest = require $this->manifestFile;
foreach (array_keys($manifest) as $blockFolder) {
register_block_type($this->buildDir . '/' . $blockFolder);
}
}
}
/**
* Injects custom component category into the Gutenberg block inserter.
*
* @param array<int, array<string, mixed>> $categories
* @return array<int, array<string, mixed>>
*/
public function registerCustomCategory(array $categories): array
{
array_unshift($categories, [
'slug' => 'agentic-lab-architecture',
'title' => 'Fullstack Agentic Lab',
'icon' => 'performance',
]);
return $categories;
}
}
(new BlockLibraryRegistrar(
buildDir: __DIR__ . '/build/blocks',
manifestFile: __DIR__ . '/build/blocks-manifest.php',
))->registerHooks();
/*
* ============================================================================
* REFERENCE ARTIFACT: build/blocks/telemetry-card/block.json (Block API v3)
* ============================================================================
* {
* "$schema": "https://schemas.wp.org/trunk/block.json",
* "apiVersion": 3,
* "name": "agentic-lab/telemetry-card",
* "version": "3.2.0",
* "title": "Architecture Telemetry Card",
* "category": "agentic-lab-architecture",
* "icon": "chart-area",
* "description": "Interactive infrastructure telemetry card supporting Interactivity API and Block Context.",
* "keywords": ["telemetry", "metrics", "benchmark", "agentic"],
* "textdomain": "agentic-lab",
* "attributes": {
* "serviceName": {
* "type": "string",
* "default": "Edge Worker Gateway"
* },
* "latencyP95Ms": {
* "type": "number",
* "default": 14
* },
* "uptimePercent": {
* "type": "number",
* "default": 99.99
* },
* "statusLevel": {
* "type": "string",
* "enum": ["optimal", "degraded", "critical"],
* "default": "optimal"
* }
* },
* "providesContext": {
* "agentic-lab/statusLevel": "statusLevel"
* },
* "usesContext": [
* "postId",
* "postType",
* "agentic-lab/environmentMode"
* ],
* "supports": {
* "html": false,
* "align": ["wide", "full"],
* "interactivity": {
* "clientNavigation": true,
* "interactive": true
* },
* "color": {
* "background": true,
* "gradients": true,
* "text": true
* },
* "spacing": {
* "padding": true,
* "margin": ["top", "bottom"],
* "blockGap": true
* },
* "typography": {
* "fontSize": true,
* "lineHeight": true
* },
* " '__experimentalBorder' -> border ": {
* "radius": true,
* "width": true,
* "color": true
* },
* "shadow": true
* },
* "editorScript": "file:./index.js",
* "editorStyle": "file:./index.css",
* "style": "file:./style-index.css",
* "viewScriptModule": "file:./view.js",
* "render": "file:./render.php"
* }
*/
Define strict types (string, number, boolean, object, array), default values, and enum sets without using brittle DOM source selectors.
Instead of building custom padding, border, or palette controls, enable color, spacing, typography, border, and shadow to bind automatically to theme.json v3.
Keep authoring React dependencies confined to editorScript, and deliver frontend interactivity via a native ES module using viewScriptModule.
Run wp-scripts build --blocks-manifest to output a single blocks-manifest.php file and mount it with wp_register_block_types_from_metadata_collection().