Creating Custom Notices in Block Editor (Gutenberg)

Recently I’ve been working with my Simple WP Crossposting plugin and I found out that in order to make the experience with the plugin even more simple I need to add some custom notices for users.

Adding those notices in Classic Editor is super simple – we just need to use admin_notices hook and there we go. But in Gutenberg things may become just a little bit more complicated.

Anyway we are going to figure everything out in this tutorial.

Creating a Simple Notice

Let’s start with something not very complicated. For example right now I am going to create a notice like this:

Custom warning notice in Gutenberg

Displaying a notice like that is actually as easy as running the code below:

wp.data.dispatch( 'core/notices' ).createNotice(
	'warning',
	'Hey, how is it going?',
	{ 
		id: 'misha-greetings-notice', 
		isDismissible: true,
	}
);

In this code please keep in mind that:

Gutenberg snackbar notices in WordPress
		type: 'snackbar',
	}
);

More parameters you can find in the official documentation.

Validation Notices (Check if Featured Image is Set)

You can also create custom validation notices to check almost everything in the post, for example you can check whether the title is provided or has a specific length.

But in this example we are just checking if post featured image is set.

WordPress Gutenberg featured image is required notice

And the code is below:

let isNoticeDisplayed = false;

wp.data.subscribe( () => {

	const featuredImage = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'featured_media' );

	if( 0 === featuredImage ) {

		if( ! isNoticeDisplayed ) {

			isNoticeDisplayed = true;

			wp.data.dispatch( 'core/editor' ).lockPostSaving( 'rudr-featured-img' );
			wp.data.dispatch( 'core/notices' ).createNotice(
				'error',
				'Do not forget about a featured image for your post!',
				{ 
					id: 'rudr-featured-img', 
					isDismissible: false 
				}
			);

		}

	} else if( isNoticeDisplayed ) {
		// if there is a featured image but the notice is still displayed
		isNoticeDisplayed = false;

		wp.data.dispatch( 'core/editor' ).unlockPostSaving( 'rudr-featured-img' );
		wp.data.dispatch( 'core/notices' ).removeNotice( 'rudr-featured-img' );

	}

} );

Some code explanation:

Displaying a Custom Notice Depending on a REST API Response

And this is actually the reason why I decided to publish a tutorial like that. In my Simple WP Crossposting plugin I needed to find a way to tell users when the connection to a sub-site is interrupted (for example an application password is removed).

create custom block editor notice in WordPress
In the right part of the screen you can see “Publish on” section where you can choose websites you would like this post to be crossposted to (this functionality is a part of my plugin). But in case an incorrect application password was provided you will see a warning like on the screenshot.

1. Store REST API errors in post meta

This is the step when the potential errors may occur. Let’s assume that we are using save_post action hook to sending a REST API request inside it, and if this request fails, we have to display a notice.

add_action( 'save_post', 'rudr_request_to_rest_api' );

function rudr_request_to_rest_api( $post_id ) {
	
	$res = wp_remote_request( ... );
	
	if( is_wp_error( $res ) ) {
		
		update_post_meta( 
			$post_id, 
			'last_error', 
			wp_json_encode(
				array(
					'code' => $res->get_error_code(),
					'message' => $res->get_error_message(),
				)
			)
		);
		
	}
}

This example is simplified and not necessary a HTTP API request should produce a WP_Error object, but it is just a demonstration. By the way, if you think there is a better way to handle errors like this, please let me know in the comments section.

2. Creating a custom REST API endpoint

The whole idea is that just after we’ve saved a post in Gutenberg an additional REST API request will be sent to check if there were any errors or not. It means that we need to register an additional REST API endpoint for that purpose.

It can be done quite easily with the register_rest_route() function.

add_action( 'rest_api_init', 'rudr_register_rest_api_route' );

function rudr_register_rest_api_route() {
	
	register_rest_route(
		'rudr/v1', // first part of the endpoint
		'latest-error', // second part of the endpoint
		array(
			'methods' => WP_REST_Server::READABLE, // GET
			'callback' => function( $request ) {
				
				$post_id = $request->get_param( 'id' );
				$last_error = get_post_meta( $post_id, 'last_error', true );
				
				if( $last_error ) {
					
					$last_error = json_decode( $last_error );
					
					return new WP_REST_Response(
						array(
							'code'    => $last_error->code,
							'message' => wp_unslash( $last_error->message ),
						)
					);

				} else {
					return new WP_REST_Response( array( 'code' => 'OK' ) );
				}

			},
			'permission_callback' => function() {
				return current_user_can( 'edit_posts' );
			},
		)
	);
	
}

Everything should be clear here, if it is not, I recommend to check register_rest_route() function documentation.

Just one thing – you can see that on line 27 that I am returning a kind of empty WP_REST_Response object, it is required because we always have to return a correct response, otherwise you are going to get some errors in your browser console.

3. Displaying the error when saving a post in Block Editor

And finally the last step, some JavaScript code to make it work in the Block Editor.

( function( wp ) {
	
	const { subscribe, select, dispatch } = wp.data;
	const { isSavingPost, getCurrentPostId } = select( 'core/editor' );
	const { addQueryArgs } = wp.url;
	let checked = true;

	subscribe( () => {

		if( isSavingPost() ) {
			checked = false;
		} else {
			if ( ! checked ) {
				checked = true;

				// some variables
				const postId = getCurrentPostId();
				const path = addQueryArgs( '/swc/v1/latest-error', { id: postId } );

				wp.apiFetch({
					path: path,
				}).then( function( response ){

					if( response.message ){
						dispatch( 'core/notices' ).createNotice(
							'warning',
							response.message,
							{
								id: 'rudr_notice',
								isDismissible: true
							}
						);
					} else {
						dispatch( 'core/notices' ).removeNotice( 'rudr_notice' );
					}

				} );
				
			}
		}
	} );
	
} )( window.wp );

This piece of code is kind of similar to what we did when created validation notices but with wp.apiFetch() call.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X