TLDR;
Blocks are rendered into static HTML markup which should be manipulated like any other HTML markup that is received from the server.
Here is how it is done:
Gutenberg blocks are graceful shortcodes like the following code. That is how they are kept in database:
<!-- wp:paragraph {"key": "value"} -->
<p>Welcome to the world of blocks.</p>
<!-- /wp:paragraph -->
They are rendered into different visual representations for server side and client side code.
On the server serve side, these shortcodes are rendered into react components by the editor for visual representation. Editor uses the outer HTML comment for matching a shortcode to a react component.
On the client side, outer html comments will be ignored and the inner HTML files will be rendered.
Remember the shortcodes, [gallery id="123" size="medium"]
which could be rendered into visual representation by tinymce plugin and you would see fully formed and functioning gallery in the editor window. The idea is same, ony this time Gutenberg editor renders slightly different shortcodes into a visual representation.
Now, the confusion arises because WordPress docs addresses these visual representations also as blocks. But source of truth for the whole rendering-serializing-parsing-re-rendering cycle is one and that is these so called "graceful shortcodes", rest is different forms and representations these shortcodes take. Say, in editor context, its is a rendered react component, on front end it is just regular html.
It is the return element of edit
function that will determine how a block will appear in the editor window:
<!-- language: lang-js -->
registerBlockType(«NAMESPACE/BLOCK_NAME», {
edit: function(props){
// Should return a react element
}
})
It is paramount to understand a block's life cycle to understand them better. Lets start from the beginning:
When you click a block icon in components panel, return of save
function will be rendered and serialized and inserted into the page.
<!-- language: lang-js -->
registerBlockType("NAMESPACE/BLOCK_NAME", {
save: function(props){
// Should return a react element
}
})
save
function should return a react element, this element will be rendered by react and serialized by the block serializer and inserted into to the post content as a block. You can check serializer.js
for how React element serialized into a block1.
<!-- wp:image -->
<figure class="wp-block-image"><img src="source.jpg" alt="" /></figure>
<!-- /wp:image -->
If it is dynamic block, save
function will return null
, so there will no content. Block will look something like this:
<!-- wp:latest-posts {"postsToShow":4,"displayPostDate":true} /-->
Notice the self closing comment:
In blocks grammar, the first one is called a static block (Block_Balanced) and the second one is dynamic block (Block_Void).
It is important to note that static blocks contain rendered content and an object of attributes. For dynamic blocks, render_callback
should be supplied to register_block_type
function during block registration.
So, when the_content
is requested, server gets the_content
and passes it through several filters before responding the request.
During this phase, attributes will be strip off from static blocks and content will be returned since static blocks have their contents already in themselves. For dynamic blocks, render_callback will be invoked and its return value will be returned as block content. That's what they mean by somehow fully-isomorphic3 in the documentation. You can check out render_block
function in Wordpress core.
When you edit the block via gutenberg's visual elements, the block will go through re-serialization process all over again and a new visual representation will be drawn onto page based on changes you've made.
<!-- wp:paragraph {"key": "value"} -->
<p>Welcome to the world of blocks.</p>
<!-- /wp:paragraph -->
Once you click the publish button, this serialized data or row data, as the documentation tells, will be save to database.
Lets say you close the page after saving. When you opened it next time, saved block will be parsed by the block parser and visual representation will be drawn onto page. You can take a look at the parser2.
During parsing, block markup will be validated against the save
function. If you changed the return of save
function between two edits, previously saved block markup will be invalid or deprecated. You can update deprecated code by providing upgrade path in your block setting in registerBlockType
. However you change the edit
function without repercussions since it controls how blocks will appear on editor screen.
Upgrade path is just an array of objects with functions and properties. Deprecated block will be checked by each element on this array, based on priority and will be migrated if block is compatible with new version, if not old version will be returned.
Now coming to your question, when request a page on the front end server will send you fully formed html. So, on the front what you get is is static html, not a react element.
So, in reality save
function has nothing to do with frontend other than creating static html wrapped in block comments <!-- wp:image --><!-- /wp:image -->
, which happens while editing content. It is never run or consulted when serving the_content
on the front end.
In order to add interactivity, you have to write code specifically targeting the front-end, just as you did before Gutenberg.
You can see the_content
in its plain text form by switching to Code Editor in the editor window using More tools and options button, vertical ellipses next to update button.
From the front-end perspective, there is no difference whether you use tinymce editor or Gutenberg editor or plain html when creating the_content
.
It is up to you enqueue another javascript file or use the one you enqueued via enqueue_block_assets
when registering block.
To use React, you have to mount your component onto document using ReactDOM. Good thing is Wordpess already provides React and ReactDOM in global space but you need to indicate the dependency when enqueuing the script.