Frontend developer focused on inclusive design

Assets loading in WordPress block

There are various ways to register blocks, but using block.json is highly recommended by WordPress. The block.json metadata file is also capable of loading default scripts and styles.

However, there are cases when block’s assets need to be loaded conditionally.

Default scripts and styles

I like to use @wordpress/create-block for block development in WordPress as it provides a simplified, structured starting point for creating blocks. The package generates various types of files for a block, including the block.json file.

The block.json metadata file offers parameters to manage scripts and styles both on the front-end (what visitors see) and back-end (where you edit content).

Metadata parameters

  • The editorScript parameter specifies the location of a script file or an enqueue handle for loading scripts in the Block Editor (back-end).
  • The script parameter specifies the location of a script file or an enqueue handle needed for both the front-end and back-end.
  • The viewScript parameter is for scripts to load on the front-end. It accepts a file location or an enqueue handle.
  • The editorStyle parameter specifies the location of a stylesheet or an enqueue handle for styling in the Block Editor (back-end).
  • The style parameter indicates the location of styles needed for both the front-end and back-end, or an enqueue handle.

Note, each parameter can accept an array, allowing to specify multiple file locations and/or script handles.

Load default scripts and styles via path

One way to load block assets is by specifying a path in the block.json file.

For the Flip Block plugin, I developed two complementary blocks. Each block includes default assets specified in the block.json file. Below is a segment code from the metadata file of the Card block:

"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"

In this snippet, three assets are being specified: editorScript, editorStyle, and style. Here's a breakdown of each line in the code snippet:

  • The 1st line specifies the JavaScript file (index.js) that will be loaded in the Block Editor (back-end) whenever the Card block is being used or edited. The file: protocol signifies that index.js is a file located in the same directory as the block.json file.
  • The 2nd line specifies the CSS stylesheet (index.css) that will be applied in the Block Editor (back-end) to style the Card block during editing. Similar to editorScript, the file: protocol signifies that index.css is a file located in the same directory as the block.json file.
  • The 3rd line specifies the CSS stylesheet (style-index.css) that will be applied both in the Block Editor (back-end) and on the front-end of the website to style the Card block. This stylesheet is loaded in both environments, ensuring consistent styling whether you're editing the block or viewing it on the live site.

It's important to note that the files referenced in this block.json snippet are located in the build folder, not the src folder.

During the build process, source files from the src folder are compiled and output to the build folder. For instance, editor.scss from the src folder would be compiled into index.css in the build folder.

Load scripts and styles via handles

There are cases when “path” approach can cause issues. In that case, “handle” approach is recommended to avoid the issues.

To load scripts and styles using handles, utilize the functions wp_register_script and wp_register_style in PHP file:

/**
 * Registers a script for a block.
 *
 * The 'wp_register_script' function is used to register a script with WordPress,
 * which can later be enqueued for use on the front-end or back-end of the site.
 *
 * @see https://developer.wordpress.org/reference/functions/wp_register_script/
 *
 * @param string $handle       A unique name for the script used to identify it.
 * @param string $src          The URL to the script file.
 * @param array  $dependencies Optional. An array of registered script handles this script depends on.
 * @param bool   $in_footer    Optional. Whether to enqueue the script before </body> instead of in the <head>.
 */
wp_register_script(
	'my-block-script',
	plugins_url( '/build/index.js', __FILE__ ),
	array( 'wp-blocks', 'wp-element', 'wp-editor' ),
	true
);

/**
 * Registers an editor style for a block.
 *
 * The 'wp_register_style' function is used to register a stylesheet with WordPress,
 * which can later be enqueued for use in the Block Editor.
 *
 * @see https://developer.wordpress.org/reference/functions/wp_register_style/
 *
 * @param string $handle       A unique name for the stylesheet used to identify it.
 * @param string $src          The URL to the stylesheet file.
 * @param array  $dependencies Optional. An array of registered stylesheet handles this stylesheet depends on.
 * @param string $version      Optional. String specifying the stylesheet version number.
 */
wp_register_style(
	'my-block-editor-style',
	plugins_url( '/build/index.css', __FILE__ ),
	array(),
	'1.0.0'
);

/**
 * Registers styles for both the front-end and back-end of a block.
 */
wp_register_style(
	'my-block-style',
	plugins_url( '/build/style-index.css', __FILE__ ),
	array(),
	'1.0.2'
);

After registering the handles, incorporate them into block.json file as follows:

"editorScript": "my-block-script",
"editorStyle": "my-block-editor-style",
"style": "my-block-style"

In this snippet, the editorScript, editorStyle, and style fields are using handles instead of file paths. These handles are identifiers that have been previously defined in PHP using the wp_register_script and wp_register_style functions.

Conditionally load block assets

The “handle” approach for loading scripts and styles is also used when there is a need to conditionally load scripts and styles for a WordPress block.

Use case example

When working on an update for the Sortable Block plugin, I came across a need to load a JavaScript file only under a certain condition.

The block has an option allowing users to select a filter date, which is optional. Upon selecting a date, a specific JavaScript file needs to be loaded on the front-end. This script is responsible for checking child elements to determine if they have expired based on the selected date.

However, if the filter date is not selected, there's no need to load this script as it would remain unused, adding unnecessary load to the page.

Solution

To load the JavaScript file, or CSS stylesheet, only when the specific block option is set, it's essential to check the block's attributes. The check for the attribute value needs to happen during the block rendering process.

There are two ways to access the render process.

The original method is to use the render_callback argument when register_block_type. The alternate method, which has been supported since WordPress 6.1, is to use the render property in block.json.

Personally, I prefer the original method:

/**
 * Register the Sortable block.
 *
 * This code snippet demonstrates how to register a new block type in WordPress.
 * The 'register_block_type' function is used, with the path to the block's files
 * and a custom render callback function specified as arguments.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
register_block_type(
	__DIR__ . '/build/container',
	array(
		'render_callback' => 'sortable_render_block_sortable_container',
	)
);

The code snippet above is from the Sortable Block plugin and demonstrates how to register a block in WordPress using the register_block_type function.

There is an array containing a key render_callback, which specifies a custom render callback function sortable_render_block_sortable_container to be used when rendering the block on the front-end.

The render callback function serves as the place to inspect the block's saved options and execute conditional loading of styles and scripts.

However, it's important to have these styles and scripts registered beforehand (as discussed earlier in the section about using handles) before using them in the render function.

/**
 * Render callback function for the Sortable Container block.
 *
 * This function checks if a specific attribute ('displayType') is set to 'date',
 * and if so, it enqueues a necessary JavaScript script to handle date-related functionalities.
 * The script is enqueued only if it hasn't been enqueued already, ensuring efficiency.
 *
 * @param array  $attributes The block attributes.
 * @param string $content    The block content.
 *
 * @return string Returns the original block content, unmodified.
 */
function sortable_render_block_sortable_container( $attributes, $content ) {
    // Check if 'displayType' is set to 'date' in the block's attributes.
    if ( isset( $attributes['filter']['displayType'] ) && 'date' === $attributes['filter']['displayType'] ) {
        // Check if 'sortable-block-core-script' is not already enqueued.
        if ( ! wp_script_is( 'sortable-block-core-script', 'enqueued' ) ) {
            // Enqueue 'sortable-block-core-script' if the conditions are met.
            wp_enqueue_script( 'sortable-block-core-script' );
        }
    }

    // Return the original block content unmodified.
    return $content;
}

The code snippet above defines a render callback function for a WordPress block. This function is triggered when the block is being rendered on the front-end. The render function takes two parameters: $attributes and $content.

  1. It checks if a particular attribute (displayType) is set to 'date' within the block's settings.
  2. If 'date' is the selected displayType, it then checks whether a script with the handle 'sortable-block-core-script' has already been enqueued.
  3. If the script hasn't been enqueued, it enqueues the script using the wp_enqueue_script function.
  4. Finally, the function returns the original block content unmodified, ensuring the block is displayed as intended on the front-end.

This setup allows for the conditional loading of a script based on the block's attribute settings, optimizing the load only when necessary.