When WordPress Gutenberg was first released, I took an instant dislike to it. Then life got busy and difficult, and I was MIA for several years. Recently, I started playing around with WordPress again, and really looked at Gutenberg a lot more closely.
I think the longer-term vision of Gutenberg, to provide simple and seamless full-site editing is a very good one. The added structure provided by blocks is also quite interesting. In the short-term, there are many types of useful blocks introduced and many more added with plugins, such as the ability to easily create tables, add buttons, add social-media links, solicit input, and more.
It seems that all this added functionality would be a big plus, especially together with the large screen (distraction free) editor mode. Why then are there so many unhappy users? Why are there over 5 million active installations of the older Classic Editor? The reason is easily seen in the screenshot below:
However, with just a few modifications that we will discuss below, the Gutenberg editor view can look like this:
To Gutenberg or Not to Gutenberg?
For me, the main issues with Gutenberg are as follows:
- Editing legacy articles causes a lot of the formatting, especially on code blocks, to get seriously messed up.
- Writing new articles is very cumbersome because the user is forced to create many blocks for simple and frequent operations such as adding paragraphs, headers, and more. This disrupts the flow of writing. Previously, I am mostly typing in the editor, whereas now, I keep breaking my flow with the constant clicking for adding blocks.
Since writing text is the most frequent thing that I do in my WordPress editor, versus say making tables or adding buttons, I need to have that process be as smooth as possible. In this way, I can focus on writing and not on the often disrupting interface for adding blocks. This makes the two weaknesses above a deal-breaker for using Gutenberg.
What I want, is to use free-form text as I did before, but combine that with Gutenberg blocks. In this way, I get the power of all the added functionality of Gutenberg, without losing any of the ease of use that I had with writing text in the previous editor. For example, I added a Gutenberg table block below and combined it with the free-form text above.
Pros of Gutenberg | Cons of Gutenberg |
New useful functionality added with blocks. | Messes up formatting of legacy articles especially code. |
Large full-screen editing mode. | Block interface greatly disrupts flow of writing text. |
Making WordPress Gutenberg Work for Me
To see if I can make WordPress Gutenberg work for me, I really looked into the Gutenberg editor javascript files with the following goals in mind:
- Stop the Gutenberg editor from messing up the formatting of my code segments. I add a lot of code on this site and I would like to use shortcodes as I did previously, as well as be able to edit legacy articles freely, without undesired changes to my code segments.
- Edit my article in code view as I did previously, without distracting HTML paragraph markups and various escaped characters.
- Save my articles as I did previously, without additional paragraph markups. I only add paragraph tags on the server side, before rendering my page.
- Stable free-form text editing. Currently, my articles can get more and more messed up the more times I edit and save. I want predictable behavior upon saving, so I don’t need to worry about constantly going back and checking if certain segments of free-form text got messed up after save.
Is all this possible? Can free-form text be made to work as before in the Gutenberg editor so that we can easily combine it with Gutenberg blocks? Would you use Gutenberg if free-form text can be properly combined with blocks? Well, you can see for yourself in the example screenshots we provided at the start of this article.
1. Stop Gutenberg from Messing-Up Code Formatting
To stop Gutenberg from messing up code formatting, we need to remove those code segments from being processed by Gutenberg’s autop and removep javascript functions. This article describes how I achieved this. The screenshot below shows the results of this particular modification.
Now, formatting spaces and tabs are preserved, and no spurious P or BR tags are added in the code segments. However, characters are still escaped and there are P and BR tags in the rest of the text, which distracts from our writing process. Ideally, I want the code view to work as before, unescaped and with P and BR tags removed.
2. Remove Distracting HTML Markups and Escaped Characters
Ultimately, we need to add these markups before rendering our web page. However, as was the case previously, we do not want to see these markups in the editor. To enable these changes, I make two small edits in the Gutenberg block-library.js file.
NOTE: We follow the same process as the previous step where we create our own copy of the file, modify it, and then replace it by using the admin_enqueue_scripts action.
Our code would look as follows:
<?php add_action( 'admin_enqueue_scripts', 'gutenberg_replace_scripts', 0 ); function gutenberg_replace_scripts() { global $wp_scripts; $wp_scripts->registered['wp-autop']->src = MY_PLUGIN_URL. 'js/autop.js'; $wp_scripts->registered['wp-block-library']->src = MY_PLUGIN_URL. 'js/block-library.js'; } ?>
We then make the following edits:
- Open our copy of block-library.js.
- Look for “function onSetup”
- Within the function, look for the “editor.on(‘loadContent'” event, and replace it with the following:
editor.on('loadContent', () => editor.setContent(Object(external_lodash_["unescape"])(content)));
Unescape is needed here because our post content is saved in escaped format for security reasons.
3. Stable Post Saves
To ensure stable post saves that don’t introduce unexpected artifacts in the visual editor, we need to make another small change to our block-library.js file.
- Open our copy of block-library.js.
- Look for “function freeform_save_save”
- Replace the return Object line of code with the following two lines:
var c = Object(external_wp_autop_["removep"])((external_lodash_["unescape"])(content)); return Object(external_wp_element_["createElement"])(external_wp_element_["RawHTML"], null, c);
This will unescape our visual editor content as well as remove autop tags. Running removep will allow our post to be saved without P and BR tags as was the case previously. This change enables stable saves from the visual Gutenberg editor.
By changing the freeform_save_save function, we ensure that our editor always sends to the server freeform text that is unescaped and free of P and BR tags. In this way, when the server escapes our post content before save, it is always operating off of clean text. In contrast, if we pass it escaped text, we will be applying another escape operation on that, which gets saved, and then gets escaped again on the next save iteration and so on, creating the possibility of a cascade of accumulated unwanted artifacts.
4. Remove Final Code Artifacts
With these changes, there are still some final unexpected artifacts added to our code block as is highlighted in the image below.
To remove these artifacts, I need to make changes to one last Gutenberg file, the blocks.js file.
- Open our own copy of blocks.js.
- Look for function serializeBlock.
- Look for if (blockName === getUnregisteredTypeHandlerName() … and add the following:
- Look for function parse.
- Make the additions below to the top of the function parse file. We make three edits in the parse function, each of which is indicated by the Shiba comment marker.
if (blockName === getUnregisteredTypeHandlerName() || !isInnerBlocks && blockName === getFreeformContentHandlerName()) { // Shiba - unescape is needed here for parse to work properly return Object(external_lodash_["unescape"])(saveContent); return saveContent; }
function parse(source, matchers) { // Shiba var str = ''; if (!matchers) { return; } // Coerce to element if ('string' === typeof source) { // Shiba str = source; var doc = getDocument(); doc.body.innerHTML = source; source = doc.body; } // Return singular value if ('function' === typeof matchers) { // Shiba - Need to check for freeform html (non block) // Just return the source and not process it. Otherwise, // <!-- and --> will be added to code. This function gets // called in StopEditing function in wp-editor.js if (str && (str.indexOf('class="wp-block') === -1) // May need to add more conditions here to capture all // freeform segments && ((str.indexOf('<p>') === 0) || (str.indexOf('<div') === 0)) ) { return str; } return matchers(source); } // Bail if we can't handle matchers ...
In the parse function we are trying to catch free-form text blocks and then return its content unchanged (i.e. without passing it through the matchers(source) function. The matchers function is the thing that adds those unwanted artifacts, so bypassing it gives us the clean result shown below.
The problem is that we cannot be too permissive here for if we are, we will erroneously group header, paragraph, and list Gutenberg blocks as free-form text. In the code above, I set the condition so that freeform text has to either start with a paragraph or div tag. You may need to add more conditions here depending on your freeform blocks.
5. Add Back Autop Before Page Render
The above changes allow us to save post content without the P and BR tags as was the case before. To properly render the page in HTML, we need to add back paragraph tags as was done previously. I add back paragraph tags by using the ‘the_content’ filter.
<?php // Add to init function add_filter( 'the_content', 'shiba_the_content', 1 ); function shiba_the_content($content) { $content = preg_replace_callback('/\[(php|html|css|js|caption)[^\]]*\][\s\S]*?\[\/\1\]/', function($matches) { $this->preserve[] = $matches[0]; return '<wp-preserve>'; }, $content); $content = wpautop($content); if (count($this->preserve)) { $content = preg_replace_callback('/<wp-preserve>/', function($matches) { return array_shift($this->preserve); }, $content); } return $content; } ?>
This entire article was written using our modified Gutenberg editor with free-form text living side by side with Gutenberg table and plugin blocks. Here are my three modified Gutenberg editor javascript files (originals taken from WordPress 5.9):
If you want to try them out, note that they are taken from WordPress 5.9, and will only work for that version of WordPress. In addition, I have optimized the WordPress Gutenberg editor according to my own workflow. If you have a different workflow and demands, there may be cases that are not taken into account in the changes above.
tom says
hi,
my experience was like the same, but for me as theme developer the “lazy blocks”(https://wordpress.org/plugins/lazy-blocks/) plugin added a backend interface for creating blocks with individual output templates and possibility to disable container markup at all (which was my main concern)
i ended up exporting stuff that work with my self-developed theme and now i am absolutely fine AND happy with Gutenberg.
Your post outlines for me what i think i have to learn the next Months 😉