register_block_type_from_metadata() │ WP 5.5.0
Registers a block type from the metadata stored in the block.json file.
Return
WP_Block_Type|false
. The registered block type on success, or false on failure.
Usage
register_block_type_from_metadata( $file_or_folder, $args );
$file_or_folder(string) (required)
Path to the JSON file with metadata definition for the block or path to the folder where the block.json file is located. If providing the path to a JSON file, the filename must end with block.json .
$args(array)
Array of block type arguments. Accepts any public property of WP_Block_Type . See WP_Block_Type::__construct() for information on accepted arguments.
Default: empty array
Examples
#1 Using WP Dashicon for the block
Build In Example
To do this, specify an icon without the prefix dashicons-
in $args in the parameter icon
:
add_action( 'init', 'wpkama_register_block' );
function wpkama_register_block(){
register_block_type(
__DIR__ . '/build.json',
[
'icon' => 'admin-home', /* omit 'dashicons-' prefix */
]
);
}
All Dashicons names: https://developer.wordpress.org/resource/dashicons/
#2 Auto-creation of blocks via .json files
Build In Example
class My_Blocks {
public function setup_hooks(): void {
add_action( 'acf/init', [ $this, 'register_blocks' ] );
add_filter( 'block_categories_all', [ $this, 'register_block_category' ] );
}
public function register_blocks(): void {
$blocks = glob( __DIR__ . '/Blocks/*/block.json');
if ( ! $blocks ) {
return;
}
foreach ( $blocks as $block ) {
register_block_type( $block );
}
}
}
( new My_Blocks() )->setup_hooks();
Example .json file:
{
"name": "ice-cream/slider",
"title": "Ice Cream Slider",
{ "description": "Simple customizable image slider",
"style": "block.css",
"category": "ice-cream",
"icon": "images-alt",
"apiVersion": 2,
"keywords": [],
"acf": {
"mode": "preview",
"renderTemplate": "render.php"
},
"styles": [],
"supports": {
"align": false,
"anchor": false,
"alignContent": false,
"color": {
"text": false,
"background": true,
"link": false
},
"alignText": false,
"fullHeight": false
}
}
Read more here: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/
#3 How to write a plugin/theme with multiple blocks
Build In Example
Creating the src
folder
Run the command:
npx @wordpress/create-block@latest my-blocks --variant=dynamic
cd my-blocks
For more details, see the manual https://developer.wordpress.org/block-editor/getting-started/tutorial/
Move the contents of the src
directory to a subdirectory, for example block-a
: src/block-a
.
Duplicate the block-a
subdirectory to create a second block, and name it, for example, block-b
.
Update the block.json
files in each subdirectory to meet the block requirements.
The structure should look like this:
my-blocks
├── package.json
├── package-lock.json
└── src
├── block-a
│ ├── block.json
│ ├── edit.js
│ ├── editor.scss
│ ├── index.js
│ ├── render.php
│ ├── style.scss
│ └── view.js
└── block-b
├── block.json
├── edit.js
├── editor.scss
├── index.js
├── render.php
├── style.scss
└── view.js
Example content of block.json
:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "create-block/block-a",
"version": "0.1.0",
"title": "Block A",
"category": "widgets",
"icon": "smiley",
"description": "Example block scaffolded with Create Block tool.",
"example": {},
"supports": {
"html": false
},
"textdomain": "wpkama",
"render": "file:./render.php",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"viewStyle": "file:./style-index.css",
"viewScript": "file:./view.js"
}
Run the command npm run build
in the my-blocks
directory. Corresponding directories will be created in the my-blocks/build
folder.
Registering blocks
Now you need to register the blocks in PHP, pointing to the corresponding directory in the build
folder:
add_action( 'init', 'wpdocs_create_blocks_mysite_block_init' );
function wpdocs_create_blocks_mysite_block_init() {
register_block_type( __DIR__ . '/build/block-a' );
register_block_type( __DIR__ . '/build/block-b' );
}
Moving the blocks folder inside the project
If your npm packages folder node_modules
is located somewhere above, and the blocks should be inside, for example in the theme folder, you can specify the paths where the sources are located and where to output the builds.
To do this, add options to the build
and start
scripts in the package.json
file:
"scripts": {
"build": "wp-scripts build --webpack-src-dir=path/to/my-blocks/src/ --output-path=path/to/my-blocks/build/ --webpack-copy-php",
"start": "wp-scripts start --webpack-src-dir=path/to/my-blocks/src/ --output-path=path/to/my-blocks/build/ --webpack-copy-php",
...
}
Now npm run build
can be run from the folder where package.json
is located, and the blocks will be built in the internal folder (where you specified).
Add Your Own Example
Changelog
Since 5.5.0
Introduced.
Since 5.7.0
Added support for textdomain field and i18n handling for all translatable fields.
Since 5.9.0
Added support for variations and viewScript fields.
Since 6.1.0
Added support for render field.
Since 6.3.0
Added selectors field.
Since 6.4.0
Added support for blockHooks field.
Since 6.5.0
Added support for allowedBlocks , viewScriptModule , and viewStyle fields.
register_block_type_from_metadata() register block type from metadata code
WP 6.6.2
function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
/*
* Get an array of metadata from a PHP file.
* This improves performance for core blocks as it's only necessary to read a single PHP file
* instead of reading a JSON file per-block, and then decoding from JSON to PHP.
* Using a static variable ensures that the metadata is only read once per request.
*/
static $core_blocks_meta;
if ( ! $core_blocks_meta ) {
$core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
}
$metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ?
trailingslashit( $file_or_folder ) . 'block.json' :
$file_or_folder;
$is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC );
// If the block is not a core block, the metadata file must exist.
$metadata_file_exists = $is_core_block || file_exists( $metadata_file );
if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
return false;
}
// Try to get metadata from the static cache for core blocks.
$metadata = array();
if ( $is_core_block ) {
$core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder );
if ( ! empty( $core_blocks_meta[ $core_block_name ] ) ) {
$metadata = $core_blocks_meta[ $core_block_name ];
}
}
// If metadata is not found in the static cache, read it from the file.
if ( $metadata_file_exists && empty( $metadata ) ) {
$metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
}
if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) {
return false;
}
$metadata['file'] = $metadata_file_exists ? wp_normalize_path( realpath( $metadata_file ) ) : null;
/**
* Filters the metadata provided for registering a block type.
*
* @since 5.7.0
*
* @param array $metadata Metadata for registering a block type.
*/
$metadata = apply_filters( 'block_type_metadata', $metadata );
// Add `style` and `editor_style` for core blocks if missing.
if ( ! empty( $metadata['name'] ) && str_starts_with( $metadata['name'], 'core/' ) ) {
$block_name = str_replace( 'core/', '', $metadata['name'] );
if ( ! isset( $metadata['style'] ) ) {
$metadata['style'] = "wp-block-$block_name";
}
if ( current_theme_supports( 'wp-block-styles' ) && wp_should_load_separate_core_block_assets() ) {
$metadata['style'] = (array) $metadata['style'];
$metadata['style'][] = "wp-block-{$block_name}-theme";
}
if ( ! isset( $metadata['editorStyle'] ) ) {
$metadata['editorStyle'] = "wp-block-{$block_name}-editor";
}
}
$settings = array();
$property_mappings = array(
'apiVersion' => 'api_version',
'name' => 'name',
'title' => 'title',
'category' => 'category',
'parent' => 'parent',
'ancestor' => 'ancestor',
'icon' => 'icon',
'description' => 'description',
'keywords' => 'keywords',
'attributes' => 'attributes',
'providesContext' => 'provides_context',
'usesContext' => 'uses_context',
'selectors' => 'selectors',
'supports' => 'supports',
'styles' => 'styles',
'variations' => 'variations',
'example' => 'example',
'allowedBlocks' => 'allowed_blocks',
);
$textdomain = ! empty( $metadata['textdomain'] ) ? $metadata['textdomain'] : null;
$i18n_schema = get_block_metadata_i18n_schema();
foreach ( $property_mappings as $key => $mapped_key ) {
if ( isset( $metadata[ $key ] ) ) {
$settings[ $mapped_key ] = $metadata[ $key ];
if ( $metadata_file_exists && $textdomain && isset( $i18n_schema->$key ) ) {
$settings[ $mapped_key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $settings[ $key ], $textdomain );
}
}
}
if ( ! empty( $metadata['render'] ) ) {
$template_path = wp_normalize_path(
realpath(
dirname( $metadata['file'] ) . '/' .
remove_block_asset_path_prefix( $metadata['render'] )
)
);
if ( $template_path ) {
/**
* Renders the block on the server.
*
* @since 6.1.0
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
*
* @return string Returns the block content.
*/
$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
ob_start();
require $template_path;
return ob_get_clean();
};
}
}
$settings = array_merge( $settings, $args );
$script_fields = array(
'editorScript' => 'editor_script_handles',
'script' => 'script_handles',
'viewScript' => 'view_script_handles',
);
foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
}
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$scripts = $metadata[ $metadata_field_name ];
$processed_scripts = array();
if ( is_array( $scripts ) ) {
for ( $index = 0; $index < count( $scripts ); $index++ ) {
$result = register_block_script_handle(
$metadata,
$metadata_field_name,
$index
);
if ( $result ) {
$processed_scripts[] = $result;
}
}
} else {
$result = register_block_script_handle(
$metadata,
$metadata_field_name
);
if ( $result ) {
$processed_scripts[] = $result;
}
}
$settings[ $settings_field_name ] = $processed_scripts;
}
}
$module_fields = array(
'viewScriptModule' => 'view_script_module_ids',
);
foreach ( $module_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
}
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$modules = $metadata[ $metadata_field_name ];
$processed_modules = array();
if ( is_array( $modules ) ) {
for ( $index = 0; $index < count( $modules ); $index++ ) {
$result = register_block_script_module_id(
$metadata,
$metadata_field_name,
$index
);
if ( $result ) {
$processed_modules[] = $result;
}
}
} else {
$result = register_block_script_module_id(
$metadata,
$metadata_field_name
);
if ( $result ) {
$processed_modules[] = $result;
}
}
$settings[ $settings_field_name ] = $processed_modules;
}
}
$style_fields = array(
'editorStyle' => 'editor_style_handles',
'style' => 'style_handles',
'viewStyle' => 'view_style_handles',
);
foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
}
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$styles = $metadata[ $metadata_field_name ];
$processed_styles = array();
if ( is_array( $styles ) ) {
for ( $index = 0; $index < count( $styles ); $index++ ) {
$result = register_block_style_handle(
$metadata,
$metadata_field_name,
$index
);
if ( $result ) {
$processed_styles[] = $result;
}
}
} else {
$result = register_block_style_handle(
$metadata,
$metadata_field_name
);
if ( $result ) {
$processed_styles[] = $result;
}
}
$settings[ $settings_field_name ] = $processed_styles;
}
}
if ( ! empty( $metadata['blockHooks'] ) ) {
/**
* Map camelCased position string (from block.json) to snake_cased block type position.
*
* @var array
*/
$position_mappings = array(
'before' => 'before',
'after' => 'after',
'firstChild' => 'first_child',
'lastChild' => 'last_child',
);
$settings['block_hooks'] = array();
foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) {
// Avoid infinite recursion (hooking to itself).
if ( $metadata['name'] === $anchor_block_name ) {
_doing_it_wrong(
__METHOD__,
__( 'Cannot hook block to itself.' ),
'6.4.0'
);
continue;
}
if ( ! isset( $position_mappings[ $position ] ) ) {
continue;
}
$settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ];
}
}
/**
* Filters the settings determined from the block type metadata.
*
* @since 5.7.0
*
* @param array $settings Array of determined settings for registering a block type.
* @param array $metadata Metadata provided for registering a block type.
*/
$settings = apply_filters( 'block_type_metadata_settings', $settings, $metadata );
$metadata['name'] = ! empty( $settings['name'] ) ? $settings['name'] : $metadata['name'];
return WP_Block_Type_Registry::get_instance()->register(
$metadata['name'],
$settings
);
}
Related Functions