Skip To Main Content

Creating a Block from the WordPress Blocks List

In this how to guide you will install and create a block using @faustwp/blocks. It will use data from the official Blocks that are available in the block editor of your WordPress site.

Prerequisites

You will need to complete both of these guides before you can create blocks with this tutorial. Make sure you have the latest version of Faust and @faustwp/blocks installed if you have already completed these guides in the past.

  1. Faust Getting Started Guide
  2. Blocks Getting Started Guide

1. Choose a block to implement

NOTE
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.

Code Block preview

2. Review the Code Block 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 your WordPress instance and create a New Post. Add a Block into the Post. For this tutorial it will be the Code Block shown below:

Code Block Editor Preview

NOTE
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 the Code Block. Once you get familiar with the block’s look and feel, let’s explore its attributes from the GraphiQL IDE.

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

NOTE
The GraphQL IDE link will show in the Admin Bar of your WordPress instance if you have properly installed WPGraphQL from the Getting Started Guide.

3.1. Search GraphQL IDE Docs for CoreCode

Within the GraphQL IDE, use the Docs to find the CoreCode Block to display its type and properties. If you chose a different Block, then search for that type instead.

After searching the Docs for CoreCode and clicking on CoreCodeAttributes, you will see the following:

Core Code block attributes documentation.

3.2. Inspect the block data via the GraphQL IDE

You can use the following query in the GraphQL IDE to inspect the Block data. Replace /posts/testing with the path to your post.

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

This will resolve into:

"data": {
    "post": {
      "editorBlocks": [
        {
          "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"
          }
        }
      ]
    }
Code language: JavaScript (javascript)

NOTE
Notice that with some of the attributes like backgroundColor, the editor used the theme.json palette slug name instead of the actual value, i.e. tertiary. It also stored the custom hardcoded styles in the style fields as a JSON string. We will 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 how to implement the block in your Faust Headless site.

4. Create the initial block and log the attributes

Follow the steps below to create a CoreCode block that logs its attributes in the console.

4.1 Create a wp-blocks folder in your Headless Site root

4.2 Create the CoreCode.js inside the wp-blocks folder

// CoreCode.js
import React from 'react';
// create CoreCode component
export default function CoreCode(props) {
  console.log(props.attributes);
  return <div>CoreCode</div>;
}

CoreCode.displayName = 'CoreCode';
// This also works
// CoreCode.config.name = 'CoreCode'
Code language: JavaScript (javascript)

NOTE
We added a displayName property here to make sure that the __typename field in the editorBlocks query matches this value. For production builds, it is required to use either a displayName="NameOfBlock" or a config.name="NameOfBlock" properties for theWordPressBlocksViewer component to resolve and render the block properly.

This component logs the block attributes to the console via the attributes prop passed through the props parameter.

4.3 Create an index.js file inside the wp-blocks folder

This file will be used to import all your blocks and then make them available as the default export.

// wp-blocks/index.js
import CoreCode from './CoreCode';
// export blocks
export default {
  'CoreCode': CoreCode,
};
Code language: JavaScript (javascript)

NOTE
By exporting the block in index.js we make it available to the WordPressBlocksProvider. We also specifically define the name of the block as ‘CoreCode’ that will match the __typename value of the block.

4.4 Create the CoreCode block fragment

You want to create a fragment that describes the request block fields and attributes. First, import { gql } from @apollo/client. Then add the CoreCode.fragments code block. Include all relevant fields for your implementation. The CoreCode file so far should look like this:

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

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

CoreCode.fragments = {
  key: `CoreCodeBlockFragment`,
  entry: gql`
    fragment CoreCodeBlockFragment on CoreCode {
      attributes {
        borderColor
        backgroundColor
        content
        style
        textColor
        fontSize
        fontFamily
        cssClassName
      }
    }
  `,
};
Code language: JavaScript (javascript)

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

4.5 Include the block fragment in your Faust template query string

NOTE
In our example, we use the block in both the Posts and Pages templates when using the Faust Template Hierarchy.

You want to include the fragment in your Faust template query string and reference the block fragment key so that when the page resolves, the payload includes the data from that block. In this example, single.js is the template. You would implement this in whatever template you are working on.

// 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) {
        ...
        editorBlocks {
            name
            __typename
            renderedHtml
            id: clientId
            parentClientId
            ...${blocks.CoreCode.fragments.key}
        }
    }
...
}
Code language: JavaScript (javascript)

Notice that in this template example we first import blocks from '../wp-blocks'. Then inside the templates gql Component.query, we include the fragment entry with ${blocks.CoreCode.fragments.entry}. Then we use the key ${blocks.CoreCode.fragments.key} inside the GetPost.

If you navigate to the page that contains this block, you should be able to inspect the properties in the console and see your block attributes that were logged by console.log(props.attributes) in CoreCode.js. In this case, we will see the CoreCode block attributes.

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

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

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.

INFO
This implementation can be found in the official Gutenberg @wordpress/block-library package. Take a look at this folder to view the file structure and contents.

Create and import block styles

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 our a helper function getStyles that creates the final inline styles for that block based on the block attributes.

INFO
If you want to learn more about the getStyles helper and how it works, you can follow this reference page here.

Finally we pass on the props.attributes?.content as a string to the code element.

1. Import the block library styles

It will contain the front-end block styles for this CoreCode block. If you are doing a different block or a custom block, you will create an scss file in styles/blocks/ for that specific block. However in this case, you need to import the Gutenberg theme styles which contain among other things the CoreCode styles:

// _blocks.scss
// WordPress block styles.
// Used in `components/ContentWrapper`

@use 'sass:math';

@import '@wordpress/base-styles/mixins';
@import '@wordpress/base-styles/colors.native';
@import '@wordpress/base-styles/z-index';
@import '@wordpress/base-styles/default-custom-properties';
@import '@wordpress/base-styles/colors';
@import '@wordpress/base-styles/variables';
@import '@wordpress/base-styles/breakpoints';
@import '@wordpress/block-library/src/style';
@import '@wordpress/block-library/src/theme';
Code language: JavaScript (javascript)

NOTE
Next.js won’t allow you to import the scss file 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.

2. Finish the CoreCode.js component.

Import getStyles and useBlocksTheme from @faustwp/blocks. These helper functions will consolidate all the block theme attributes and create a final inline stylesheet rules for that block.

Create and set styles to getStyles(props.attributes) to get the proper formatting of the attributes. Add in the HTML markup for your block and use the className and styles variables for proper styling.

In this case for the CoreCode block, we added the pre and code markup from WordPress Core and output the contents of the block using ${props.attributes?.content}. If you chose to complete the CoreCode block with us, your file should match what we have here:

// wp-blocks/CoreCode.js
import React from 'react';
// import graphql and our getStyles helpers from Faust
import { gql } from '@apollo/client';
import { getStyles, useBlocksTheme } from '@faustwp/blocks';

export default function CoreCode(props) {
  // use getStyles utility to process the props.attributes properly
  const theme = useBlocksTheme();
  const style = getStyles(theme, props);
  const { attributes } = props;
  return (
    // This markup is from the WordPress CoreCode block
    // add the className and styles to the markup
    // add props.attributes?.content to the markup
    <pre className={attributes?.cssClassName} style={styles}>
      <code>{`${attributes?.content}`}</code>
    </pre>
  );
}
// add fragment
CoreCode.fragments = {
  key: `CoreCodeBlockFragment`,
  entry: gql`
    fragment CoreCodeBlockFragment on CoreCode {
      attributes {
        borderColor
        backgroundColor
        content
        style
        textColor
        fontSize
        fontFamily
        cssClassName
      }
    }
  `,
};
CoreCode.displayName = 'CoreCode';Code language: JavaScript (javascript)

NOTE
Now that you have the styles configured correctly, you can navigate to your Headless Website and check that the block matches the styles in the WordPress editor. Make sure to tweak the block in the WordPress Editor and verify that the changes are being reflected in the Headless Website.

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
    );
}
Code language: PHP (php)

NOTE
If you include those extensions in a custom plugin, your Headless Website 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, i.e. Material UI or ChakraUI.

Bear in mind that this will almost always result in degraded user editing experience as the styles of the WordPress Editor view won’t match the styles of the rendered headless 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 using jQuery and document selectors.

To be able to review the bundled JavaScript assets, 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 Headless Website so it’s up to you how you want to handle the pros and cons of each approach.