Working with Custom Post Fields in Custom Gutenberg Blocks.


Integrating Custom Post Fields with Gutenberg Blocks offers WordPress users a powerful method for extending the functionality and customization of their content creation process. Custom post fields, also known as post meta, provide a means to store additional data associated with posts or pages, while Gutenberg Blocks revolutionize content creation by introducing modular components. This synergy enables developers to craft custom blocks that seamlessly interact with and display data from these fields, enhancing the editing experience and empowering users to create richer, more dynamic content within the familiar Gutenberg interface.

We will then discuss why we might want to bridge the gap between custom post fields and our Gutenberg blocks, how the custom post field shoud be structured and how we can pass data back and forth between the post and our blocks.

Custom Post Fields

One of the ways that WordPress content (posts, pages, etc.) is extended is by the inclussion of custom post fields. WordPress has it’s own simplified, free-form interface for adding fields and there are third party plugins such as Advanced Custom Fields that make it easy to configure fairly complicated arrays of information, that can be filtered by post type, category or even specific posts.

Prior to the inclusion of the Gutenberg block editor, custom post fields would typically be displayed by adding them to a specific place in the post template or perhaps including them in the body of the post with a shortcode. In either case, under this paradigm the post itself is effectively the smallest configurable node of information in the website.

An image of a block to reference custom Gutenberg blocks

Enter Gutenberg Blocks

This changes somewhat with the advent of Gutenberg and the idea of a post being a collection of “blocks.” Rather than a single WYSIWG field for post content, it is now broken down to individual block elements. 

Some of these are fairly simple, such as a paragraph. Others can be more complex, such as cover image. Blocks can be nested and arranged into reusable “block patterns,” offering considerable flexibility within the content individual posts. Developers can also design their own custom blocks.

Each block stores its own data within the content field of a post. What’s nifty is that most of the data is stored in the blocks HTML content and attributes itself, but additional information might be included as unrendered comments as part of the code of the block. Thus, the entire block and its data is self-contained and can be easily moved around within the Gutenberg editor, or even stored and reused throughout the website.

By it’s own, Gutenberg blocks have replaced some of the use cases where custom post fields would be used. For example, if we want to include a sidebar element on a webpage, we don’t necessarily have to add it as a custom post field in a custom template. Now we have to option to construct it using blocks instead.

Bridging the Gap between Custom Post Fields and Gutenberg Blocks

There are a couple cases I can think of where someone would want to use blocks, but store the data in the post itself. One is when you want to be able to easily access the data outside of the content, such as in a post archive or table of contents. Custom Post Fields and other meta data are relatively easy to extract using the get_post_meta() and other functions. 

Another case could be where you have a relatively complex data object or array, which can be awkward to store with a Gutteberg block attribute. In my case, I’m currently working with block that displays data within a spreadsheet. I’d rather not have this large data object carried around in the block comments.

In order to reference custom post fields in a Gutenberg block, there are several key steps you need to take. First, you need to make sure that your custom post field is accessible to blocks. Second, you need to define a “usesContext” property in your block schema that references your custom post field. Third, you need to include code in the Edit portion of your block to retrieve and store data into the custom post field. Finally, you need to provide a reference to that custom post field so it can be shown when the block is rendered to the viewer.

Making Custom Post Fields Accesible to Gutenberg Blocks

There are a couple of important settings for a post type and custom post field that necessary for the fields to be accessible to your Gutenberg block. These include having your post type “supports” property array include “custom-fields” and the “show_in_rest” property set to “true.”

In addition, you will need to include the property, “show_in_rest”, as part of your register_post_meta function used to define the custom field. Also, if your custom post field is an array, you will need to provide a schema for the “show_in_rest” property, otherwise it can simply be set to “true”

For example, registering a simple custom post field might look like this:

<?php
register_post_meta( 'post', 'my_custom_post_field', [
  'type' => 'string',
  'single' => true,
  'show_in_rest' => true
] );

A custom post field that includes an array, may look like this:

<?php
register_post_meta( 'post', 'another_field', [
  'type' => 'object',
  'single' => true,
  'show_in_rest' => [
    'schema' => [
      'type' => 'object',
      'properties' => array(
        'T' => array('type' => 'string'),
        'v' => array('type' => 'integer'),
        'vw' => array('type' => 'number')
      ),
    ],
  ]
]);

When working with arrays in the “show_in_rest” schema, associative arrays are of type “object” where as numerical arrays are of type “array” More information on this can be found here.

Context

The WordPress Block API Reference has a discussion of Context as it relates to passing data between blocks. Using context is similar with custom post fields, except in this case we are simply referencing the parent post id.

For example, our block.json schema file might look like this:

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "create-block/example-block",
  "version": "0.1.0",
  "title": "Example Block",
  "category": "widgets",
  "icon": "chart-area",
  "description": "Example Block",
  "example": {},
  "attributes": {
    "postID": {
      "type": "number"
    }
  },
  "usesContext": ["postId"],
  "supports": {
    "html": false
  },
  "textdomain": "example-block",
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css",
  "viewScript": "file:./view.js"
}

In this case, the usesContext property array includes a “postId” which we use to reference the parent post in the block edit. Also, make note of the attribute “postID” which is used to pass the post id when the block is rendered since Context is not available to be saved.

Retrieving and Storing Custom Post Fields in the Block

In order to retrieve the custom post field data within the Edit function of the block, we need to do the following:

First, we need to import the userSelect and useEntityProp hooks. 

import { useSelect } from '@wordpress/data';
import { useEntityProp } from '@wordpress/core-data'; //used to store to post meta

This allows us to be able to get the custom post field, “my_custom_post_field” as part of the Edit function, for example:

export default function Edit( { attributes, setAttributes,context: { postId } } ) {

  const blockProps = useBlockProps();

  // post meta

  const postType = useSelect((select) => select('core/editor').getCurrentPostType(), []);

  const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta', postId);

  setAttributes( { postID: postId } ); // here the postId is passed to the corresponding attribute

  const metaFieldValue = meta.my_custom_post_field !== null ? meta. my_custom_post_field : {};

  const updateMetaValue = ( newValue ) => {
    setMeta( { ...meta, my_custom_post_field: newValue } );
  };

The above shows the block attribute “postID” is set to the postId We will use this in order to be able to pass this information to Javascript when the block is rendered as part of the post. For example, our save function looks like this:

export default function save({ attributes }) {
  return (
    <div { ...useBlockProps.save({ attributes }) }>
      <div id="chart" data-post-id={ attributes.postID }></div>
    </div>
  );
}

We can then reference the post id in our front end Javascript using:

document.getElementById('chart').dataset.postId

It is also possible (and probably easier) to use dynamic rendering to bring in custom post fields into a rendered block. For example, to achieve the above, our alternative render.php file could look like:

<?php
postID = $block->context['postId'];
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
  <div id="chart" data-post-id="<?php echo esc_html( $postID ); ?>"></div>
</div>

Closing Thoughts on using Custom Fields with Gutenberg Blocks

Using Custom Post Fields within Gutenberg Blocks gives flexibility to developers in how important content is stored and displayed in a WordPress website. The technique can bridge the gap between providing flexibility and ease of use of custom post fields within the editor while retaining the benefits of having structured and easily referenced post data in other contexts, such as summary pages or archives. It also helps with blocks with complex data that are better stored ouside of the content field.