Roll your own specialized WordPress block editor

Here is how you can create your own simplified WordPress block editor for specialized applications using React and the REST API.

I am, in general, a fan of the WordPress block editor and how it has progressed. However, I’ve learned over time that all the options available within the block editor can be overwhelming to newbies.

This has proven to be particularly true to the community of Toastmasters whom I’ve introduced to WordPress through the Toastmost.org website hosting service. Sometimes, asking them to dive into the block editor is inappropriate and/or overkill for relatively simple tasks they want to perform. That’s prompted me to create a couple of simplified block editors, including one for editing RSVP forms for events that’s included with the latest update to RSVPMaker.

To be clear, I’m not trying to reproduce all the work of the good folks running the Gutenberg block editor project. My editors don’t come close to the sophistication of the block editor, but they offer a simpler user experience for accomplishing specific tasks. That is, working with RSVP forms and Toastmasters agendas. These documents can contain other sorts of blocks — paragraphs, headings, and images — but the advantage of my specialized editors is that they are focused. Rather than having to hunt through the block inserter to find my specialized blocks — amidst all the blocks geared toward blog posts and web pages — I can present just the blocks relevant to my application.

For example, my equivalent of the inserter for this RSVP form editor just offers form fields:

Custom block inserter

For each block, there are also options to move blocks up or down within the document, change attributes, or delete the block.

Options for form fields

The effect winds up being effectively the same as working with those same blocks in the WordPress block editor, where the field options are shown in the sidebar. Although my custom-built editor doesn’t allow you to add other blocks, it can move them up or down within the document without corrupting that content.

Same form in the standard block editor

The secret to making this work is turning the WordPress document you want to work with into an array of JSON objects you can manipulate in your client-side application. You then send the altered version back to the server as JSON and have your server-side API turn that data back into block format that can be saved as the updated body of the post in question. Naturally, this requires some attention to detail to make sure the technique can’t be used to rewrite your home page into some hacker’s manifesto. But the tools for doing it securely are available within the WordPress platform.

Turning the content of any WordPress document into JSON turns out to be easy, with the use of a function called parse_blocks, shown here in an excerpt from my REST API code.

$form_id = (empty($_GET['form_id'])) ? $rsvp_options['rsvp_form'] : intval($_GET['form_id']); 
$form = get_post($form_id);
$response['form_id'] = $form_id;
$response['form'] = parse_blocks($form->post_content);
...
return new WP_REST_Response( $response, 200 );

This results in the data being downloaded in this format:

Downloading the data as JSON

On the client side, I’ve got a React app — my first real foray into using React outside of creating WordPress editor blocks — that I use to add, delete, and rearrange items in the block array, preserving the structure of blockName, attrs (attributes) object, innerHTML (string) and arrays for innerBlocks and innerContent.

The altered array of block objects can then be posted back to the server, as shown below. In this example, I’m moving the Email field above First Name and Last Name.

Posting a new array of blocks

On the server side, I catch this input, transform it back into blocks format, and use it to update the post.

$json = file_get_contents('php://input');
$data = json_decode(trim($json));
$output = '';
foreach($data->form as $index => $block) {
	if($block->blockName)
	$output .= rsvpBlockDataOutput($block, $post_id);
}
$updated['ID'] = $form_id;
$updated['post_content'] = $output;
wp_update_post($updated);

The function doing the work of formatting the content into block data — the comments-plus-JSON format that goes into the body of the post — is shown below. I suspect that there is a similar function in WordPress core that I could be using instead, except that I haven’t found it yet. Meanwhile, this seems to work perfectly well for every block I’ve tried it on so far, including those with nested innerBlocks content.

<?php
function rsvpBlockDataOutput($block, $post_id) {
if(empty($block))
return;
$attrs = ($block->attrs) ? json_encode($block->attrs) : '';
if(!empty($block->innerHTML) || (!empty($block->innerBlocks) && sizeof($block->innerBlocks)) ) {
$output = sprintf('<!– wp:%s %s –>',$block->blockName,$attrs)."\n";
if(!empty($block->innerHTML))
$output .= $block->innerHTML."\n";
if(!empty($block->innerBlocks) && is_array($block->innerBlocks) && sizeof($block->innerBlocks)) {
foreach($block->innerBlocks as $innerblock) {
$output .= jsonBlockDataOutput($innerblock,$post_id);
}
}
$output .= sprintf('<!– /wp:%s –>',$block->blockName)."\n\n";
}
else
$output = sprintf('<!– wp:%s %s /–>',$block->blockName,$attrs)."\n\n";
return $output;
}

I’m omitting some details for the sake of making these examples clearer, such as the code for creating a new post rather than updating an existing one, but you can find my API code here and the JavaScript and script enqueuing functions here.

For client-server communication, I’m using the combination of React Query and Axios. React Query is supposed to simplify a few things, although I stumbled over many details such as getting optimistic updates to work and not using the async / await keywords correctly on my API get and post functions. I found this tutorial helpful in figuring out that part.

Let me know if you find this useful.

Leave a Reply