• Block: server side rendering

    Read more

    A convenient way to display the block in the editor :

    import ServerSideRender from '@wordpress/server-side-render';
    
    export default function Edit( { attributes, setAttributes } ) {
    
    return (
    
         <ServerSideRender
              block="dev/some-block"
              attributes={attributes}
         />
    
    );
    
    }

    Without module : <wp.blockEditor.ServerSideRender block="custom/menu-display" attributes={attributes} />

    Source: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-server-side-render/

  • block: context

    Read more

    Used to create a context of data, for example between parent / children blocks.

    Example, making the post id accessible, in block.json:

    {
    	"$schema": "https://schemas.wp.org/trunk/block.json",
        ...
        "usesContext": [ "postId" ]
    }

    In render.php :

    $post_id = $block->context['postId'];

    In edit.json :

    export default function Edit( { attributes, setAttributes, context } ) {
    
    // context.postId
    
    }
  • block: core data

    Read more

    How to fetch data from a block : in edit.js

    import { useSelect } from '@wordpress/data';
    
    const SomeComponent = ( { attributes, setAttributes } ) => {
    
        const taxonomies = useSelect( select => select( 'core' ).getTaxonomies(), [] );
    
    
    ...
    
    
    }

    Sources :

  • API interactivity

    Read more

    In block.json : { supports: { interactivity: true }, viewScriptModule: file:./view.js }

    1/ create directive

    data-wp-interactive="namespace"

    data-wp-context='{"foo": "bar"}' → local state

    data-wp-class--{class-name} the class presence will be tied to a value

    data-wp-bind--{attribute-name}

    data-wp-on--{event-name}

    2/ create a store

    • state
    • actions
    • callback
    • getElement()
    • getContext()

    Example

    view.js :

    import { store, getContext } from '@wordpress/interactivity';
    
    store( 'wpdsfr', {
    	actions: {
    		toggle: () => {
    			const context = getContext();
    			context.isOpen = ! context.isOpen;
    		},
    	}
    } );

    render.php

    <section
    	data-wp-interactive="wpdsfr"
    	data-wp-context='{ "isOpen": false}'
    	<?php echo get_block_wrapper_attributes(); ?>>
    
    	<h3	data-wp-on--click="actions.toggle">
    		item title
    	</h3>
    
    	<div data-wp-class--is-open="context.isOpen">
    		<?php echo $content; ?>
    	</div>
    </section>

    Sources:

  • block: RichText edition

    Read more

    With an attribute in block.json as attributes: { foo: { type: string, default: bar } }

    In edit.js :

    import { useBlockProps, RichText } from '@wordpress/block-editor';
    
    export default function Edit( { attributes, setAttributes } ) {
    	const blockProps = useBlockProps();
    
    	return (
    		<RichText
    			{ ...useBlockProps() }
    			tagName="p"
    			onChange={ val => setAttributes( { foo: val } ) }
    			value={ attributes.content }
    			placeholder="Enter some text here..."
    		/>
    	);
    }

    In render.php

    echo attributes['foo'];

    Sources:

  • block: setting sidebar options

    Read more

    in block.json:

    { ...
      "attributes": {
        "foo": {
          "type": "string",
          "default": "bar"
        }
      }
    }

    in render.php, the data is accessible simply with echo $attributes['foo'];

    In edit.js

    import { TextControl, Panel, PanelBody } from '@wordpress/components';
    import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
    
    export default function Edit( { attributes, setAttributes } ) {
    	const blockProps = useBlockProps();
    
    	return (
    		<p { ...blockProps }>
    			<InspectorControls key="setting">
    				<Panel>
    					<PanelBody title="Champs" initialOpen={true}>
    						<TextControl className="blocks-base-control__input"
    							label={"Foo"}
    							value={attributes.foo}
    							onChange={(val) => setAttributes({foo: val})}
    						/>
    					</PanelBody>
    				</Panel>
    			</InspectorControls>
    			{ attributes.heading }
    		</p>
    	);
    }

    Adding toggle button, for an attribute defined as { foo: { type: "boolean", default: true } }

     <ToggleControl
        label="Toggle Content"
        checked={ showContent }
        onChange={ (newValue) => setAttributes( { foo: newValue } )  }
    />

    A range button :

    <InspectorControls>
        <PanelBody>
            <RangeControl
                label="Columns"
                value={ columnCount }
                onChange={ onChangeColumnCount }
                min={ 2 }
                max={ 6 }
             />
       </PanelBody>
    </InspectorControls>

    NB: To extend useBlockProps hooks, just send new data as parameter:

    { ...useBlockProps( { style: columnStyles } ) }

    Sources:

  • Create a new block

    Read more

    npx @wordpress/create-block@latest blockname --template @wordpress/create-block-interactive-template

    Some other options :

    npx @wordpress/create-block@latest blockname --variant dynamic
    npx @wordpress/create-block@latest blockname --variant dynamic --template es5 → no node module, great for simple block, remove –variant for a static one.

    To register the block : register_block_type( ${path of directory containing the block.json} );

    In case of several block, optional but perfomance improving : wp_register_block_metadata_collection( $path, $manifest );

    To generate a manifest : wp-scripts build-blocks-manifest

    Sources:

  • API binding

    Read more

    Populate dynamically blocks (button, image, paragraph and heading), by setting the source, from meta field or php logic.

    meta field

    <!-- wp:paragraph {
    	"metadata":{
    		"bindings":{
    			"content":{
    				"source":"core/post-meta",
    				"args":{
    					"key":"book-genre"
    				}
    			}
    		}
    	}
    } -->
    <p></p>
    <!-- /wp:paragraph -->
    register_meta(
        'post',
        'book-genre',
        array(
            'show_in_rest' => true,
            'single'       => true,
            'type'         => 'string',
            'default'      => 'Default text field',
        )
    );

    php logic

    $source_name = 'namespace/name';
    $source_properties = array(
    	'label'	=> 'Name',
    	'get_value_callback' => 'fn',
    	'uses_context' => ['postId']
    );
    
    register_block_bindings_source(
    	string $source_name,
    	array $source_properties
    );
    
    projectslug_bindings_callback(
    	array $source_args, 
    	WP_Block $block_instance, 
    	string $attribute_name
    );

    Source: https://make.wordpress.org/core/2024/03/06/new-feature-the-block-bindings-api/

  • Block variations

    Read more

    Block styles: create specific look variations with CSS

    Block variations: preset a block configuration.

    Example:

    wp.blocks.registerBlockVariation(
    	'core/quote',
    	{
    		name: 'dev-name',
    		title: 'Title',
    		icon: 'format-status',
    		isDefault: true,
    		attributes: {
    			templateLock: 'all',
    			className: 'dev-classname'
    		},
    		innerBlocks: [
    			[
    				'core/image',
    				{
    					className: 'wp-block-image alignleft'
    				}
    			],
    			[
    				'core/paragraph',
    				{
    					className: 'dev-paragraph'
    				}
    			]
    		]
    	}
    );

    Source:

    • https://fullsiteediting.com/lessons/block-variations/
    • https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/
    • https://developer.wordpress.org/news/2023/08/29/an-introduction-to-block-variations/
  • Override blocks style

    Read more

    To override block styles for rules that can’t be changed through theme.json (complexe rules or non core blocks) , one can use dev__blocks__enqueue_styles function:

    add_action( 'init', 'dev__blocks__enqueue_styles' );
    
    function dev__blocks__enqueue_styles() {
    	foreach ( glob( get_stylesheet_directory() . '/assets/css/blocks-overriding/*', GLOB_ONLYDIR ) as $directory) {
    		$namespace = substr( strrchr( $directory, '/' ), 1 );
    		foreach ( glob( $directory . '/*.css' ) as $file) {
    			$filename = pathinfo( $file, PATHINFO_FILENAME );
    			wp_enqueue_block_style(
    				$namespace . '/' . $filename,
    				array(
    					'handle'	=> 'dw-override--' . $namespace . '-' . $filename,
    					'src'		=> get_stylesheet_directory_uri() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
    					'path'		=> get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css',
    					'ver'		=> filemtime( get_stylesheet_directory() . '/assets/css/blocks-overriding/' . $namespace . '/' . $filename . '.css' )
    				)
    			);
    		}
    	}
    }

    Source : https://developer.wordpress.org/reference/functions/wp_enqueue_block_style/

  • Dynamically generate a block

    Read more
    $block = array(
    	'blockName'	=> 'dev/blockname',
    	'attrs' => array(
    		'name'	=> 'dev/blockname',
    		'data'	=> array(
    			'ACF__field' => get_field( 'ACF__field' )
    		)
    	),
    );
    $html_output = render_block($block);

    To know what data send as parameter for the $block object, one can use the render_block filter

    add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 );
    function dev__paragraph_add_block( $block_content, $block ){
    
    	if ( 'dw/icon' === $block['blockName'] ){
    		echo '<pre>';
    		var_dump($block);
    		echo '</pre>';
    	}
    
    	return $block_content;
    
    }

    Source : https://www.itsupportguides.com/knowledge-base/wordpress/using-wordpress-render_block-php-function/

  • block supports properties

    Read more

    To use the supports properties, first enable them in the block.json.

    {
    	"$schema": "https://schemas.wp.org/trunk/block.json",
    	"apiVersion": 3,
    	
     ...
    
    	"supports": {
    		"color": {
    			"text": true,
    			"background": true
    		}
    	},
    	"acf": {
    		"mode": "preview",
    		"renderTemplate": "./render.php"
    	}
    }

    Then in render.php :

    <?php
    
    $classes = array();
    
    if ( ! empty( $block['textColor'] ) ) {
    //	$classes = array_merge( $classes, explode( ' ', $block['className'] ) );
    	$classes[] = 'has-text-color';
    	$classes[] = 'has-' . $block['textColor'] . '-color';
    }
    
    echo '<div class="' . esc_attr( join( ' ', $classes ) ) . '"></div>';

    Or simply use the function get_block_wrapper_attributes( string[] $extra_attributes = array() ): string for e.g. to get the classes :

    preg_match( '/class="(.*?)"/', get_block_wrapper_attributes(), $matches);
    classes = $matches[1];

    Sources:

  • Block rendering filter

    Read more
    add_filter( 'render_block', 'dev__paragraph_add_block', 10, 2 );
    
    function dev__paragraph_add_block( $block_content, $block ){
    
    	if ( 'core/paragraph' === $block['blockName'] ){
    		$block_content = new WP_HTML_Tag_Processor( $block_content );
            $block_content->next_tag(); /* first tag should always be ul or ol */
            $block_content->add_class( 'wp-block-paragraph' );
            $block_content->get_updated_html();
    	}
    
    	return $block_content;
    
    }
  • InnerBlock

    Read more

    Interesting attributes : allowedBlocks, template, InnerBlocks.

    Example of a devblock/render.php

    <?php
    
    $classes_container = array( 'block__container' );
    $classes_innerblock = array( 'block__innerblock' );
    
    if ( !empty( $block['className'] ) ){
    	$classes_root = array_merge( $classes_root, explode( ' ', $block['className'] ) );
    }
    
    $allowed_blocks = array( 'namespace/blockname' );
    
    $template = array(
    	array(
    		'namespace/blockname',
    		array(
    			'className'		=> 'some-class'
    		)
    	)
    );
    
    ?>
    
    <div class="<?php echo join( ' ', $classes_container ); ?>">
    	<InnerBlocks
    		class="<?php echo join( ' ', $classes_innerblock ); ?>"
    		allowedBlocks="<?php echo esc_attr( wp_json_encode( $allowed_blocks ) ); ?>"
    		template="<?php echo esc_attr( wp_json_encode( $template ) ); ?>" />
    
    </div>
    
  • Creating a block

    Read more

    Prototype:register_block_type( string|WP_Block_Type $block_type, array $args = array() ): WP_Block_Type|false

    add_action( 'init', 'dev__register_blocks', 5 );
    
    function dev__register_blocks() {
    	register_block_type( __DIR__ . '/some_block_dir' );
    }

    The block.json file :

    {
        "$schema": "https://schemas.wp.org/trunk/block.json",
        "apiVersion": 3,
        "name": "namespace/blockname",
        "title": "Notice",
        "category": "text",
        "parent": [ "core/group" ],
        "icon": "star",
        "description": "Shows warning, error or success notices...",
        "keywords": [ "alert", "message" ],
        "version": "1.0.3",
        "textdomain": "my-plugin",
        "editorScript": "file:./index.js",
        "script": "file:./script.js",
        "viewScript": [ "file:./view.js", "example-shared-view-script" ],
        "editorStyle": "file:./index.css",
        "style": [ "file:./style.css", "example-shared-style" ],
        "render": "file:./render.php",
    	"supports": {
    		"className": true,
    		"customClassName": true,
    		"jsx": true			// to use innerBlocks,
    		"inserter": true	// will be accessible only programatically
    	},
    
    	// ACF options: 
    	"acf": {
    		"mode": "preview",
    		"renderTemplate": "render.php"
    	},
    
    	"example": { "attributes": { "mode": "preview" } }
    
    
    }
    

    Sources: