0

I’m working on a custom dynamic Gutenberg block. I’d like it to act like a normal rich text paragraph (e.g., can be split or merged as needed), rather than have multiple “paragraphs” included in one paragraph with <br /> breaks to break the text up.

The reason for building a custom block is because I want to control certain aspects of the formatting for this block type—set a specific font family for this block, possibly change the background color, etc. And I need it to be dynamic (rather than static) so I can modify the formatting in the future as needed without having to go into hundreds of individual posts to update the code.

I’ve gotten to where my plugin works; I can type in rich text, and have it save properly, but I can’t figure out how to get each new paragraph to be its own block. If I try to enable the onSplit= function (see below), I’m not even able to insert <br />-separated paragraphs anymore.

My block.json file:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "custom-blocks/bc-synopsis",
    "version": "0.1.0",
    "title": "Back Cover Synopsis",
    "category": "text",
    "attributes": {
        "content": {
            "type": "string",
            "default": ""
        },
        "placeholder": {
            "type": "string"
        }
    },
    "example": {
        "attributes": {
            "content": "Block especially suited for back cover synopsis."
        }
    },
    "textdomain": "custom-blocks",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css",
    "render": "file:./render.php",
    "keywords": [ "synopsis" ]
}

My edit.js file (where I think my problem is):

/**
 * useBlockProps is a React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps
 *
 * RichText is a component that allows developers to render a contenteditable input,
 * providing users with the option to format block content to make it bold, italics,
 * linked, or use other formatting.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/richtext/
 */
import { useBlockProps, RichText } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @param {Object}   param0
 * @param {Object}   param0.attributes
 * @param {string}   param0.attributes.content
 * @param {Function} param0.setAttributes
 * @return {WPElement} Element to render.
 */
export default function Edit( { 
    attributes: { content }, 
    placeholder, 
    mergeBlocks,
    onReplace,
    onRemove,
    setAttributes } ) {

    return (
        <RichText 
            identifier="content"
            { ...useBlockProps({className: 'back-cover-content'}) }
            tagName="p"
            value={ content }
            onChange={ ( newContent ) =>
                    setAttributes( { content: newContent } )
            }
        /** Trying to get this...but it's breaking
            onSplit={ ( value, isOriginal ) => {
                let newAttributes;

                if ( isOriginal || value ) {
                    newAttributes = {
                        ...attributes,
                        content: value,
                    };
                }

                const block = createBlock( name, newAttributes );

                if ( isOriginal ) {
                    block.clientId = clientId;
                }

                return block;
            } }*/
            onMerge={ mergeBlocks }
            onReplace={ onReplace }
            onRemove={ onRemove }
            placeholder={ placeholder || __( 'Paste your back cover content in here' ) }
        />
    );
}

And finally, my index.js file:

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import metadata from './block.json';

/**
 * 'export' from Gutenberg...paragraph settings (modified)
 */
export const settings = {
    merge( attributes, attributesToMerge ) {
        return {
            content:
                ( attributes.content || '' ) +
                ( attributesToMerge.content || '' ),
        };
    },
}

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
    /**
     * @see ./edit.js
     */
    edit: Edit,
} );

Of course, there are other files, like my render.php, but I don’t think that has anything to do with this particular problem. If I can work out how to type a paragraph in this new block, hit enter and instantly a new block of the same style is created and I can continue writing paragraphs that way (until I switch to anther block type), I’d be happy.

Any help would be appreciated!

2
  • 1
    Based on your description you might just be better served by adding a block style to the paragraph block: developer.wordpress.org/block-editor/reference-guides/block-api/…. Nov 24, 2023 at 8:49
  • Jacob is correct, a block style or a block variation is how this is supposed to work, you can replace nearly all of your code with 4 line piece of JS then everything else can be done with CSS or even via theme.json, there's no need for a new block or onSplit/onMerge events
    – Tom J Nowell
    Nov 24, 2023 at 9:01

1 Answer 1

2

Based on the use case that lead you to this, the answer is:

You don't, that's what block styles and variations are for.

Your entire block could be eliminated and replaced with this:

wp.blocks.registerBlockStyle(
    'core/paragraph',
    {
        name: 'bc-synopsis',
        label: 'Back Cover Synopsis',
    }
);
.is-style-bc-synopsis {
    // styling etc...
}

Benefits:

  • You can change your styling across the entire site at any time via CSS/SCSS
  • Gutenberg will provide BC Synopsis as an option for transforms/insertions, and even show previews in some cases
  • When the block editor UI gets updated your stuff will automatically migrate without breaking
  • This gives you options for tight integration with global styling, the stylebook UI, and theme.json
  • If you change themes or migrate the site, or the code that registers the style is removed accidentally, everything falls back gracefully to a standard paragraph styling so nothing is broken and data loss is avoided

Here's one of the current style pickers in WP 6.4, there are more places in the UI to show this:

enter image description here

Variations

You also have the option of a block variation. This is when a block can have a variant with its own name, description, and icon, but preserves all the functionality of the original, while still showing in the block inserter as if it were an independent block.

For example this is how the core embed block works, there is no Youtube block or Facebook embed block, they're all variations of core/embed and it's the values of the attributes that determine which variation WP shows.

Registering a variant is very similar:

wp.blocks.registerBlockVariation(
    'core/paragraph',
    {
        name: 'bc-synopsis',
        title: 'Back Cover Synopsis',
    }
);

A General Note On Building Blocks

It's tempting to build a brand new block for everything, this gets expensive and cumbersome but it's unnecessary.

Most of these things can be decomposed into block patterns and block styles, you don't need a hero image block when a cover block style and a heading block will work just as well and a block pattern makes it easy to insert.

Take this synopsis block as an example, as core improves things and adds new features you're going to have to go back and update the block to enable these or things will feel super weird. Core is talking about adding collaborative editing in the near future, imagine your block doesn't support part of the GUI without a code update? Consider how none of that matters if you used a block pattern or a block style and it all just worked for free.

There are situations where making a new block makes sense, but this isn't one of them, not everything needs to be a new block.

Further Reading

Block styles:

Block variations:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.