I need an unordered list, but with options selected for each list item. To achieve this I've created a custom block called icon-list which is a ul element with InnerBlocks, which allows only my custom icon-list-item block as a child. My icon-list-item block is just a RichText element with tagName: li and some controls. This all works fine, but currently pressing the enter key adds a line break to the current icon-list-item block - what I'd really like is for the enter key to insert a new icon-list-item block. Similarly pressing the backspace key when at the start of a icon-list-item block should remove that block - the exact same functionality of the core/list block.

I've had a dig through the Gutenberg docs and source and found that I need to implement the onSplit and onReplace properties for my RichText element, but it's not clear to me how I actually do this. So far I have:

el(RichText, {
    tagName     : 'li',
    className   : 'icon--' + props.attributes.icon,
    value       : props.attributes.content,
    placeholder : props.attributes.placeholder,
    onChange    : function(value) {
        props.setAttributes({ content: value })
    onSplit     : function(value) {
        if (!value) {
            return createBlock('tutti/icon-list-item');

        return createBlock('tutti/icon-list-item', {
            content: value,
    onReplace   : function() {
        // something

... obviously I'm completely lost when it comes to the onReplace function. Any pointers would be very much appreciated!

  • Did you try setting multiline property of RichText to true? This should insert new element instead of inserting break line.
    – Lovor
    Commented Dec 29, 2022 at 18:18

2 Answers 2


Not tested. I just was browsing looking for something else. But I would try something like this:

onReplace: function() {
   var myID    = props.clientId;

This is how I implemented a list-like element which behaves like a native list element. I did this because of a deprecation notice in the RichText component. The multiline property, which could be used to make this kind of behaviour work, will be removed in version 6.3 and it is advised to use InnerBlocks instead.

const blockSettings = {
    * @param attributes Attribute object of blockA
    * @param attributesToMerge Attribute object of blockB
    * @returns {} Attribute object after the merge
  merge: (attributes, attributesToMerge) => {
    return {
      content: (attributes.content || "") +
               (attributesToMerge.content || ""),
  edit: (props) => {
    const blockProps = useBlockProps({
      // Define blog poperties

    return el(RichText, {
      ...blockProps, // Block properties not necessary for this example
      identifier: "content", // This is needed to prevent an error message
      onSplit: (value, isOriginal) => {
        let newAttributes = {}

        if (isOriginal || value) {
          newAttributes = {
            content: value,

        const block = createBlock("custom/my-list--item", newAttributes)

        // Set first element's clientId to original clientId to preserve it
        if (isOriginal) {
          block.clientId = props.clientId

        return block
      // Functions provided in blockProps by useBlockProps()
      onReplace: props.onReplace,
      onMerge: props.mergeBlocks,
      onRemove: props.onRemove,

The code is borrowed from Gutenberg's own core/paragraph block (/packages/block-library/src/paragraph).

The props-object you receive from useBlockProps-function imported from wp.editor in your edit-function already contains functions for onReplace, onRemove and mergeBlocks for the onMerge-event.
Source: /packages/block-library/src/paragraph/edit.js line 152

In addition to the edit-function, you have to declare a merge-function in your block's settings. It will receive the attributes of both blocks and return the attributes of the merged block. If you don't declare merge, the merge will fail without an error message.
Source: /packages/block-library/src/paragraph/index.js line 43

The RichText also needs an identifier-prop. Without it, an error is thrown.
Source: /packages/block-library/src/paragraph/edit.js line 127

The onSplit-function sets the clientId of the original list-item to the value of props.clientId, because onSplit replaces the original block and therefore generates a new id.

// Pseudo code
originalBlockA = {
  clientId: "A",

const { blockA, blockB } = onSplit(originalBlockA)

blockA.clientId == "B"
blockB.clientId == "C"

Source: /packages/block-library/src/paragraph/edit.js line 134

The functionality itself is kind of documented in the README.md, but the crucial part, the identifier-property and merge-function are missing.

The need for a merge-function can be found in the core/block-editor-data-store: /packages/block-editor/src/store/actions.js line 1025

The need for an identifier-property is more or less documented in /packages/block-editor/src/store/selectors.js. It is used in /packages/block-editor/src/store/actions.js on line 1057 and the attributeKey is checked on line 1073.

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.