Skip to main content

Creating a Block from the WordPress Blocks list

In this tutorial you will install and create a block using @faustwp/blocks that is using data from the official Blocks that are available in the block editor of your WordPress site. It assumes you have already installed both the @faustwp/blocks and wp-graphql-content-blocks plugin.

1. Set up a @faustwp/blocks

If you have not already done so, you will need to follow the Getting Started guide so that you are ready to create new blocks.

2. Pick a block that you want to implement

The Core Editor Blocks contain a list of useful blocks for editing and publishing content. All of the official blocks include a block.json with defined attributes, so the wp-graphql-content-blocks plugin will automatically expose those as GraphQL fields.

For this tutorial, we are going to implement the Code Block that renders a formatted block of code:

3. Review the Block design in the Editor

Before you try to implement the block, it's useful to get an idea of how the block is configured within the editor. You want to create a few sample blocks and test their options.

Navigate to the editor by creating a new Post and insert the Block into the page:

Code BLock Preview

You can expand the block sidebar options to change some of its styling. For example, you can configure the Color, Dimensions, Border, and Size settings for that block. Once you get familiar with the block's look and feel, let's explore its attributes from the GraphQL IDE.

4. Review the Block data and attributes in the GraphQL IDE

Within the GraphQL IDE, use the Docs to find the type of block to display its type and properties. There should be a CoreCode that contains all the important fields we can query.

Code Block Block documentation in GraphQL IDE

Expand the CoreCode to see the block attributes as well:

Code Block Block attributes documentation in GraphQL IDE

There are a lot of attributes associated with that block. You can use the following query to inspect the block data:

{
post(id: "/posts/testing", idType: URI) {
contentBlocks {
renderedHtml
...on CoreCode {
attributes {
borderColor
backgroundColor
content
style
textColor
fontSize
}
}
}
}
}

This will resolve into:

"data": {
"post": {
"contentBlocks": [
{
"renderedHtml": "\n<pre class=\"wp-block-code has-border-color has-tertiary-background-color has-text-color has-background has-small-font-size\" style=\"border-color:#333333;border-width:2px;color:#333333\"><code>// Computes the nth Fibonacci number\nfunction fib(n) {\n var a = 0, b = 1, c;\n if (n &lt; 3) {\n if (n &lt; 0) return fib(-n);\n if (n === 0) return 0;\n return 1;\n }\n while (--n)\n c = a + b, a = b, b = c;\n return c;\n}</code></pre>\n",
"attributes": {
"borderColor": null,
"backgroundColor": "tertiary",
"content": "// Computes the nth Fibonacci number\nfunction fib(n) {\n var a = 0, b = 1, c;\n if (n &lt; 3) {\n if (n &lt; 0) return fib(-n);\n if (n === 0) return 0;\n return 1;\n }\n while (--n)\n c = a + b, a = b, b = c;\n return c;\n}",
"style": "{\"color\":{\"text\":\"#333333\"},\"border\":{\"color\":\"#333333\",\"width\":\"2px\"}}",
"textColor": null,
"fontSize": "small"
}
}
]
}

Notice that with some of the attributes like backgroundColor, the editor used the theme.json palette slug name instead of the actual value. It also stored the custom hardcoded styles in the style fields as a JSON string. We will have to handle them appropriately when developing the associated block. Once you get familiar with how to query the necessary data from that block, let's see now how to implement the block in the Decoupled site.

5. Create the initial block

Inside the codebase of your application, create the basic block structure that simply prints the block's attributes. The steps required to achieve that are the following:

5.1 Create the block and save it inside the wp-blocks folder

wp-blocks/CoreCode.js
export default function CoreCode(props) {
console.log(props.attributes);
return <div>CoreCode</div>
}

This component prints the block attributes via the props passed as a parameter.

5.2 Export the block in the index.js

wp-blocks/index.js
import CoreCode from './CoreCode';
export default {
CoreCode
}

By exporting the block in the index.js we make it available in the WordPressBlocksProvider. You can also use a different name as long as it matches either the Block name or the __typename fields.

This also works:

wp-blocks/index.js
import CoreCode from './CoreCode';
export default {
'core/code': CoreCode
}

5.3 Create the block fragment

You want to create a fragment that describes the request block fields and attributes. Ideally you want to include all the relevant fields for the implementation part:

wp-blocks/CoreCode.js
import {gql} from '@apollo/client';
...
CoreCode.fragments = {
key: `CoreCodeBlockFragment`,
entry: gql`
fragment CoreCodeBlockFragment on CoreCode {
attributes {
borderColor
backgroundColor
content
style
textColor
fontSize
fontFamily
}
}
`,
};

We chose to attach the fragment as a property of the function component itself. This follows best practice for creating colocated fragments.

5.4 Include the block fragment into the page query string

You want to include the fragment in the page query string and reference the block fragment key so that when the page resolves, the payload includes the data from that block.

In our example we use it in both the Posts and Pages templates when using the Faust Template Hierarchy:

wp-templates/single.js
import blocks from '../wp-blocks';
export default function Component(props) {
...
}
Component.query = gql`
...
${blocks.CoreCode.fragments.entry}
query GetPost(
...
post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) {
...
contentBlocks {
name
__typename
renderedHtml
id: nodeId
parentId
...${blocks.CoreCode.fragments.key}
}
}
...
}

If you navigate to the page that contains this block, you should be able to inspect the properties in the console:

Code Code preview of the block attributes in the console

Now you are ready to design the block based on the original block's look and feel.

6. Implement the Block using the provided block attributes and the Editor view

The last part is to implement the Block by using the block attributes and the view in the editor to construct the final HTML markup.

Since the block you want to implement already consists of React elements that the Editor uses, you can review the plugin implementation of this block and try to port the parts that render the block in the frontend.

This implementation is found in the official Gutenberg @wordpress/block-library package. Take a look at this folder.

You are interested in the following files:

style.scss: Contains the front-end block styles. You can definitely include those in our global styles:

style.scss
.wp-block-code {
// This block has customizable padding, border-box makes that more predictable.
box-sizing: border-box;

// Provide a minimum of overflow handling and ensure the code markup inherits
// the font-family set on pre.
code {
display: block;
font-family: inherit;
overflow-wrap: break-word;
white-space: pre-wrap;
}
}
...

block.js: Contains the block attributes specification.

save.js: According to the docs, the save function defines the way in which the different attributes should be combined into the final markup, which is then serialized into post_content.

Most of the time, the save function contains the actual implementation of the block, so you can take this markup and adopt it. This way, the design of the block in the editor will match the design in the Decoupled site.

note

Since the block is static most of the time, the save.js should be provided. Sometimes the save function is not provided or it returns null. When this happens, the component is considered dynamic, aka it renders in the server. So you want to check if the register_block_type provides an implementation for rendering the block. In that case, the render_callback will provide the markup for the block using PHP. You can take this implementation and translate it to React.

Since the save function is provided with that block we have a viable strategy that will save us time without having to figure out the actual implementation.

So go ahead and provide the implementation of the Block based on the save function. You will need to remove the references of the RichText and useBlockProps imports as you won't be needing those.

wp-blocks/CoreCode.js
import { gql } from '@apollo/client';
import getStyles from '../utilities/getStyles.js';

export default function CoreCode(props) {
const className = 'wp-block-code';
const styles = getStyles(props.attributes)
return (
<pre className={className} style={styles}>
<code>{`${props.attributes?.content}`}</code>
</pre>
);
}

A few important things to consider here:

  • First we assign the same className wp-block-code for the block so that we can apply the same styles as the Editor.
  • Then we use a helper getStyles that creates the final inline styles for that block based on the block attributes.
  • Finally we pass on the props.attributes?.content as a string to the code element.

Let's delve into the getStyles helper function and see what it does:

utilities/getStyles.js
import { colorsMap, fontSizesMap } from '../styles/variables.js';
import { cssToReactStyle } from './cssToReactStyle.js';
import { compileCSS } from '@wordpress/style-engine';

export default function getStyles(attributes) {
return {
backgroundColor: colorsMap[attributes?.backgroundColor],
color: colorsMap[attributes?.textColor],
fontSize: fontSizesMap[attributes?.fontSize],
borderColor: colorsMap[attributes?.borderColor],
...(attributes?.style && cssToReactStyle(compileCSS(JSON.parse(attributes.style)))),
};
}

This function takes the blocks attributes and maps them into inline React styles using the following helpers:

  • colorsMap, fontSizesMap: Map the slug color and font values to associated colors and font sizes based on the current theme.json.
  • compileCSS: A helper function from the @wordpress/style-engine package that parses the JSON object containing the block styles and returns an inline style string.
  • cssToReactStyle: Another helper function that translates the inline style string into a React inline styles object.

Below is the implementation of those functions in order:

styles/variables.js
// Contains the Theme.js palette colors
export const colorsMap = {
'primary': '#9DFF20',
'secondary': '#345C00',
'tertiary': '#f6f6f6',
'foreground': '#000000',
'background': '#ffffff',
'cyan-bluish-gray': '#abb8c3',
'pale-pink': '#f78da7',
'luminous-vivid-amber': '#fcb900',
'pale-cyan-blue': '#8ed1fc',
'vivid-cyan-blue': '#0693e3',
'vivid-purple': '#9b51e0',
'vivid-red': '#cf2e2e',
'light-green-cyan': '#7bdcb5',
}

export const fontSizesMap = {
'small': '1rem',
'normal': '1.5rem',
'large': '1.75rem'
}
utilities/cssToReactStyle.js
import isObject from 'lodash/isObject';
import map from 'lodash/map';
import camelCase from 'lodash/camelCase';

export const cssToReactStyle = (css) => {
// If object is given, return object (could be react style object mistakenly provided)
if (isObject(css)) {
return css;
}

// If falsy, then probably empty string or null, nothing to be done there
if (!css) {
return {};
}

// Only accepts strings
if (typeof css !== 'string') {
console.error(`Unexpected type "${typeof css}" when expecting string, with value "${css}"`);
return {};
}

const style = {};
const rules = css.split(';');
map(rules, (rule) => {
let [key, value] = rule.split(':');

if (key && value) {
key = key.trim();
value = value.trim();

style[camelCase(key)] = value;
}
});

return style;
};

If you don't want to include the @wordpress/style-engine package in your final bundle, you can still map the JSON object manually into the associated styles. If you take a look at this style object after the style is parsed:

JSON.parse(style)
{
border: {
color: "#000"
},
color: {
text: "#333"
}
}

Mapping into the React styles object would then be:

  const parsed = JSON.parse(style);
return {
borderColor: parsed?.border?.color
color: parsed?.text?.color,
}

One final detail: don't forget to include the global scss for the block so that the right styles are applied. You can create a new folder for block styles inside the styles folder and import it there:

styles/blocks.scss
// WordPress block styles.
...
@import './blocks/CoreCode' // import block styles here

Now that you have the styles configured correctly, you can navigate into the Headless site's page and check that the block matches the styles with the editor view:

Code block demo of the block rendered in the Decoupled Website

Make sure to tweak the block in the editor and verify that the changes are being reflected in the website.

note

Next.js won't allow you to import the styles.scss directly into the component since it requires all global CSS to be imported from the pages directory only. See the related error message. So you need to either import the styles from a global module or translate the styles to use Component-Level CSS.

Further Considerations

What if the block is missing some attributes?

If the block does not have any attributes or has only a few of them declared in the block.json, you can still try to extend the block API by declaring additional attributes for that block. Follow the filters reference guide, to create a block that uses the additional_block_attributes property. The attributes will be available to query from that block.

class CoreCode extends WPGraphQL\ContentBlocks\Blocks\Block
{
protected ?array $additional_block_attributes = array(
// Write your attributes here
);
}
note

If you include those extensions in a custom plugin, your Decoupled application is dependent on the inclusion of this plugin. You need to make sure you bundle them together, otherwise the queries you perform in the application will fail.

Can I style the block differently?

Yes, you can style the block in many ways, choosing to ignore some of the attributes altogether. You can also use an external React Library to style the block. For example, use Material UI or ChakraUI. Bear in mind that this will almost always result in degraded user editing experience as the styles of the Editor view won't match the styles of the rendered page.

What if the block contains custom JavaScript assets?

Some Core blocks include JavaScript assets that are injected into the WordPress page so that they can run in the front view. Typically a lot of Dynamic Blocks use that functionality since they have no way to include user interactivity. Since React is not normally bundled as a dependency in the browser, the JavaScript code usually consists of good old jQuery and document selectors.

In that case you want to be able to review the bundled JavaScript assets and choose one of the following options:

  • Include them in your code: This is not recommended as React does not play well with plain JavaScript and jQuery so you can expect lots of problems.

  • Rewrite them as React components: You can attempt to rewrite the code into React. If the bundled code can be understood and rewritten with low effort then you can make that commitment.

  • Use an equivalent React Component from a library: A simpler alternative is to find a compatible React Package and use that instead of trying to replicate the blocks interactivity. Sometimes this is a viable strategy as it frees the developer from implementing the functionality from scratch.

Inevitably this is a common pain point when trying to leverage the power of Blocks in the Decoupled Website so it's up to you how you want to handle the pros and cons of each approach.