Skip to content

Gutenberg

Mark Howells-Mead edited this page Apr 3, 2024 · 40 revisions

Docs, posts and reference

Blocks

Errors

Minified React error #321

23.5.2022: this error was caused when trying to use a component using withSelect from within the save function of a custom block. It's not possible to use async calls within the save function; the HTML generated here has to be generated synchronously.

Code examples

Starting point for development

List all available blocks

Run wp.blocks.getBlockTypes().forEach(function (data) {console.log(data.name);}); in the browser console when the editor is open.

Monitor changes to categories

Gutenberg-Taxonomies

List all blocks in the current page

Run wp.data.select('core/editor').getBlocks() in the browser console when the editor is open.

Alignment

To force a block to only be available with full or wide alignment…

  • don't add any align options to the block component configuration
  • add the appropriate CSS class to the block wrapper's HTML
  • dynamically apply the appropriate data-align attribute in the componentDidMount class function.
const {Component} = wp.element;



edit: class extends Component {
	constructor(props) {
		super(...arguments);
		this.props = props;
	}

	componentDidMount(){
		document.querySelector('#block-' + this.props.clientId).setAttribute('data-align', 'wide');
	}

	render() {
		return 

Custom alignments

wp.hooks.addFilter(
    'blocks.registerBlockType',
    'my-theme/namespace',
    function( settings, name ) {
        if ( name === 'core/paragraph' ) {
            return lodash.assign( {}, settings, {
                supports: lodash.assign( {}, settings.supports, {
                    align: ['medium','wide','full'],
                } ),
            } );
        }
        return settings;
    }
);

Alignment in server-side rendered blocks

<?php

register_block_type('shb/demo', [
    'attributes' => [
        'align' => [
            'type' => 'string',
            'enum' => ['wide', 'full'],
        ]
    ],

  …

if (!empty($align = $attributes['align'] ?? '')) {
    $align = "align{$align}";
    }

<div <?php echo get_block_wrapper_attributes(['class' => $align]); ?>>

Attributes

InnerBlocks

Copy manually-built group
Attributes

Get all attributes of the specified innerBlocks object.

wp.data.select('core/block-editor').getBlock('87711d0c-b1f5-4609-bbb0-f3a78b275620').innerBlocks

onChange

Update title when alignment changes
componentDidUpdate(prevProps){
	if(prevProps.attributes.align !== this.props.attributes.align){
		// do something
		// this.props.setAttributes({title: `RE-ALIGNED: ${this.props.attributes.align}`});
	}
}

style

Get the block's selected style in the save function.

const { className } = props.attributes;
const isStyle = RegExp(/is-style-/);
const styleName = isStyle.test(className) ? className.replace(isStyle, '') : null;
Use different LazySizes image when alignment changes
componentDidUpdate(prevProps){
	if(prevProps.attributes.align !== this.props.attributes.align){
		// You have to use a local variable here. Using this.props.attributes.imagesize
		// to determine the imagesize doesn't work
		let imagesize = this.props.attributes.align === 'wide' ? 'panorama' : 'full';
		this.props.setAttributes({imagesize: imagesize});
		getLazySrcs(this.props.attributes.image.id, imagesize).then(image => this.props.setAttributes({image}));
	}
}

Core

Third-party

Own (work)

Blocks

  • Post Selector (Single-select dropdown of all (custom) posts, ordered by title.

Components

ServerSideRender

Content

Editor

Example code

Patterns

Patterns are groups of Blocks, which can be inserted by an author in one action (e.g. clicking on a button or preview image). Gutenberg Patterns

Templates

  • Register a Gutenberg Template, which contains a pre-defined set of other Blocks. e.g. if the user often creates the same content for a specific type of Post.

Translation

JavaScript translations are read in from enqueued scripts, not from source files. Because the built scripts are JS and minified by Webpack, the regular __ and _x function names are replaced.

Exclusions

Create the POT file with relevant exclusions via the command line. Only the generated client-facing JavaScript files in e.g. assets/dist should be scanned.

wp i18n make-pot . languages/shp_flatcare_redirect.pot --exclude="assets/src,assets/gulp,gulpfile.babel.js"

Load script translations from JSON

Example for a simple plugin implementation, April 2024. The priority 100 appears to be essential for scripts loaded in the editor using block.json.

// Load editor (JS) translations
// Caution, different path definition than for the frontend
function shp_block_editor_set_script_translations()
{
	$script_handle = generate_block_asset_handle('shp/my-block', 'editorScript');
	wp_set_script_translations($script_handle, 'shp_my_block', plugin_dir_path(__FILE__) . 'languages');
}
add_action('init', 'shp_block_editor_set_script_translations', 100);

Notes and references

  • Core ref.
  • The enqueued source file path is used in order to generate an MD5 hash within the file name. (e.g. assets/dist/blocks/blocks.js. WordPress core always uses the unminified path.) If the file is created correctly, check the MD5 hash and the path being used. A double-slash in the path will cause the wrong MD5 path to be used.
  • Make sure wp-i18n is a dependency of your Gutenberg script.
  • Call wp_set_script_translations on the init hook. (This is already in my base Theme.)
  • Make a new POT file using wp i18n make-pot . languages/sht.pot in the Theme directory.
  • Make a new PO file using cp sht.pot sht-fr_FR.po.
  • Call wp i18n make-json sht-fr_FR.po --no-purge in the languages folder to parse the PO file.

Error with wp i18n make-pot

_ "Uncaught Error: Maximum function nesting level of '256' reached, aborting"_

Solution: If using Local By Flywheel (with xdebug), increase xdebug.max_nesting_level to 512 or 1024. Change this in e.g. conf/php/php.ini.hbs and then restart the server.

Articles

Tutorials

Widgets

HTML filter rendered block

add_filter('render_block', function ($block_content, $block) {
	// Target core/* and core-embed/* blocks.
	if (preg_match('~^core/|core-embed/~', $block['blockName'])) {
		$block_content = sprintf('<div class="some__class">%s</div>', $block_content);
	}
	return $block_content;
}, PHP_INT_MAX - 1, 2);

Get options and ACF options via REST API in the Editor

To get the WordPress Options from within the Editor (e.g. for use in a Block), use the following.

import api from '@wordpress/api';



api.loadPromise.then(() => {
    settings = new api.models.Settings();
    settings.fetch().then(response => {
        console.log(response);
    });
});

To add a custom field to the REST response, use the register_setting function. The option key is usually e.g. sht_myfield but must be prefixed if the field is registered using ACF's acf_add_local_field_group function.

add_action('init', [$this, 'registerPublicOptions']);

public function registerPublicOptions()
{
    // Field registered using WP
    register_setting('options', 'sht_my_acf_field', [
        'type' => 'string',
        'sanitize_callback' => 'sanitize_text_field',
        'show_in_rest' => true,
        'default' => ''
    ]);

    // Field registered using ACF
    register_setting('options', 'options_sht_my_acf_field', [
        'type' => 'string',
        'sanitize_callback' => 'sanitize_text_field',
        'show_in_rest' => true,
        'default' => ''
    ]);
}

Disable full screen editing mode

if (wp.data) {
	wp.data.select( "core/edit-post" ).isFeatureActive( "fullscreenMode" ) && wp.data.dispatch( "core/edit-post" ).toggleFeature( "fullscreenMode" );
}

Transforms

Subscribing to changes in the block editor

e.g. if you want to show a list of all the headings in the block editor within a custom Table of Contents block.

import { _x } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { getBlockDefaultClassName, registerBlockType } from '@wordpress/blocks';
import { Disabled } from '@wordpress/components';
import { select, subscribe } from '@wordpress/data';
import { useState } from '@wordpress/element';

import block_json from '../../../../block.json';
const { name: block_name } = block_json;
const classNameBase = getBlockDefaultClassName(block_name);

registerBlockType(block_name, {
	edit: () => {
		const blockProps = useBlockProps();
		const [headings, setHeadings] = useState([]);

		// Watch for changes to the block list and update the headings list
		subscribe(() => {
			const selected_blocks = select('core/block-editor')
				.getBlocks()
				.filter(function (block) {
					return block.name === 'core/heading';
				});

			setHeadings(selected_blocks);
		});

		// Create the list of headings from the list of blocks
		const headingsList = headings.map((heading, index) => (
			<li key={index} className={`${classNameBase}__entry`}>
				<a dangerouslySetInnerHTML={{ __html: heading.attributes.content }} />
			</li>
		));

		return (
			<div {...blockProps}>
				<Disabled>
					<ul className={`${classNameBase}__entries`}>{headingsList}</ul>
				</Disabled>
			</div>
		);
	},
});
Clone this wiki locally