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
    No fields configured for 🔨 Ready for Implementation.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions