Skip to content

Support Block Manifest Generation for Improved Performance #475

@fabiankaegy

Description

@fabiankaegy

Summary

WordPress 6.7 introduced wp_register_block_metadata_collection() and WordPress 6.8 added wp_register_block_types_from_metadata_collection() to improve block registration performance. These APIs allow plugins to register blocks from a pre-compiled PHP manifest file instead of parsing individual block.json files at runtime.

10up-toolkit should support generating this manifest file as part of its build process.

The Problem

Currently, when WordPress registers blocks, it must:

  1. Locate each block.json file on disk
  2. Read the file contents
  3. Parse the JSON
  4. Process the metadata

For plugins with multiple blocks, this filesystem I/O and JSON parsing happens on every request, adding unnecessary overhead.

The Solution: Block Manifests

A block manifest is a single PHP file that contains all block metadata as a PHP array. Since PHP can load arrays directly without parsing, this eliminates the JSON parsing overhead and reduces filesystem reads to a single file.

Manifest File Format

The blocks-manifest.php file returns an associative array where keys are block directory names and values are the block metadata:

<?php
// This file is generated. Do not modify it manually.
return array(
    'accordion' => array(
        '$schema' => 'https://schemas.wp.org/trunk/block.json',
        'apiVersion' => 3,
        'name' => 'my-plugin/accordion',
        'title' => 'Accordion',
        'category' => 'design',
        'icon' => 'menu',
        'editorScript' => 'file:./index.js',
        'editorStyle' => 'file:./index.css',
        'style' => 'file:./style-index.css',
        'render' => 'file:./render.php',
        // ... rest of block.json contents
    ),
    'tabs' => array(
        '$schema' => 'https://schemas.wp.org/trunk/block.json',
        'apiVersion' => 3,
        'name' => 'my-plugin/tabs',
        'title' => 'Tabs',
        // ... rest of block.json contents
    ),
);

Registration Code

With the manifest, registration becomes a single function call:

function my_plugin_register_blocks() {
    $blocks_dir = __DIR__ . '/build/blocks';
    $manifest   = __DIR__ . '/build/blocks-manifest.php';

    // WordPress 6.8+: Single call registers all blocks
    if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
        wp_register_block_types_from_metadata_collection( $blocks_dir, $manifest );
        return;
    }

    // WordPress 6.7: Register collection, then loop
    if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
        wp_register_block_metadata_collection( $blocks_dir, $manifest );
        $blocks = require $manifest;
        foreach ( array_keys( $blocks ) as $block_slug ) {
            register_block_type_from_metadata( $blocks_dir . '/' . $block_slug );
        }
        return;
    }

    // WordPress < 6.7: Traditional registration
    $blocks = require $manifest;
    foreach ( array_keys( $blocks ) as $block_slug ) {
        register_block_type( $blocks_dir . '/' . $block_slug );
    }
}
add_action( 'init', 'my_plugin_register_blocks' );

Proposal for 10up-toolkit

1. Add Manifest Generation to Build Process

Generate blocks-manifest.php automatically when building blocks:

# Current
10up-toolkit build

# Would also generate blocks-manifest.php in the output directory

This should:

  • Scan all block directories for block.json files
  • Combine them into a single PHP array
  • Output to a configurable location (default: build/blocks-manifest.php)

2. Configuration Options

Allow configuration via 10up-toolkit.config.js:

module.exports = {
  blocksManifest: {
    // Enable/disable manifest generation (default: true)
    enabled: true,

    // Input directory containing blocks (default: 'build/blocks' or detected)
    input: 'build/blocks',

    // Output path for manifest file
    output: 'build/blocks-manifest.php',

    // Optional: Custom transformation for manifest entries
    // transform: (blockMetadata, blockSlug) => blockMetadata,
  }
};

3. Watch Mode Support

Regenerate the manifest when block.json files change during development:

10up-toolkit start
# Watches for block.json changes and regenerates manifest

Implementation Considerations

Directory Structure Detection

10up-toolkit should detect where blocks are located. Common patterns:

# Pattern 1: Blocks in subdirectories
src/blocks/accordion/block.json
src/blocks/tabs/block.json

# Pattern 2: Blocks at root
src/block.json (single block)

# Pattern 3: Multiple entry points
src/blocks/editor/accordion/block.json

Handling render Callbacks

The wp_register_block_types_from_metadata_collection() function doesn't support render_callback directly. Projects using dynamic blocks with PHP render callbacks need to use the block_type_metadata_settings filter:

add_filter( 'block_type_metadata_settings', function( $settings, $metadata ) {
    if ( isset( $metadata['name'] ) && str_starts_with( $metadata['name'], 'my-plugin/' ) ) {
        // Add render callback based on block name
        $block_slug = str_replace( 'my-plugin/', '', $metadata['name'] );
        $render_file = __DIR__ . "/build/blocks/{$block_slug}/render.php";

        if ( file_exists( $render_file ) ) {
            $settings['render_callback'] = function( $attributes, $content, $block ) use ( $render_file ) {
                ob_start();
                include $render_file;
                return ob_get_clean();
            };
        }
    }
    return $settings;
}, 10, 2 );

This is worth documenting but shouldn't affect manifest generation.

Block Slug Conflicts

The manifest uses directory names as keys. If a project has blocks in directories with identical names (e.g., blocks/editor/card and blocks/frontend/card), only one will be included. This is a known limitation of the WordPress manifest format.

10up-toolkit should either:

  • Warn when duplicate directory names are detected
  • Use full relative paths as keys (would require changes to registration code)

Comparison with @wordpress/scripts

The @wordpress/scripts package already supports this via:

{
  "scripts": {
    "build": "wp-scripts build --blocks-manifest",
    "start": "wp-scripts start --blocks-manifest"
  }
}

10up-toolkit should provide equivalent functionality with similar ease of use.

Benefits

  1. Performance: Eliminates JSON parsing and reduces filesystem reads on every request
  2. Simpler registration: One function call registers all blocks
  3. Future-proof: Aligns with WordPress core's recommended approach
  4. Backward compatible: Manifest can be used with fallback logic for older WordPress versions

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions