So you’re writing a list in the editor, and you hit Enter. And automatically, you get another bullet point!
Have you ever wondered how that happens? Would you like to make your own blocks do something like that, and not just with list items?
Well, that is what this article is here to help you with. It will probably make more sense to you if you’ve written some blocks before.
Three things usually happen.
When you’re editing a Gutenberg block, hitting Enter will usually do one of three things:
- Create a new block that matches the one you just wrote.
- Add a line break.
- Or add a new paragraph, because a paragraph is the default block.
The video below shows examples of all three.
When you hit enter to get a new block, you are using Gutenberg’s RichText component. The documentation, and people who write about WordPress, call this behavior splitting. While hitting enter at the end of a block adds a new block, hitting enter in the middle of the block splits it in two.
You need to use two functions for splitting, but there’s also a third function you’ll find handy to let users to reverse the split. That’s called merging.
- onSplit – Used to create the new block (required)
- onReplace – Used to add the new block – (required). Blocks should receive this as a prop already and often supplying this default will suffice.
- onMerge – Used to stick blocks together (optional). Blocks should receive a
mergeBlocks
prop already which can be used.
You can pass these functions as props to the RichText
components.
onSplit
onSplit receives the portion of the value getting split, and whether or not that portion comes after the original block (i.e., if whether the user is going to hit enter at the beginning of the content or not). It should return a new block, which will be called twice, once for each portion of the split.
Given a string of “Test” :
- Hitting enter at the beginning means onSplit will produce
''
andfalse
on the first call, and then"Test”
andtrue
on the second call. - Hitting enter in the middle means onSplit will get
‘Te’
andtrue
on the first call, and then‘st’
andfalse
on the second call. - Hitting enter at the end means onSplit will get
‘Test’
andtrue
on the first call, and then''
andfalse
on the second call.
In all cases, the value given should return a block. So a typical implementation will be inside a block edit component and look something like this:
<RichText
onSplit={ ( value, isAfterOriginal ) =>
createBlock( 'block/name', { ...attributes, text: value } );
}
/>
attributes
being the attributes prop passed into the edit component.
onReplace
onReplace
receives an array that holds the blocks returned from onSplit
. It also gets the index to select and the initial position.
It shouldn’t return anything; it should add the blocks to the editor. A simple implementation will be inside a block edit component and look something like this:
<RichText
onReplace={ ( blocks, indexToSelect, initialPosition ) =>
replaceBlocks( clientId, blocks, indexToSelect, initialPosition );
}
/>
Where replaceBlocks
comes from the block-editor store and clientId
is the clientId
of the current block.
The default onReplace prop does something a little like the above. Unless you need something wildly different, you might as well set it as a RichText prop and not bother to reinvent it.
onMerge
onMerge
captures the direction of the merge. Like onReplace
, it shouldn’t return anything, either. Instead, it should just find the blocks you want to merge in the editor—and merge them.
Your block should already receive a mergeBlocks prop. And you can certainly use that. But be aware that you’ll ultimately call the block’s merge
function, which will pass the attributes from the two blocks you want to merge. So you will need to assign the block merge
function in the block index.js file. That will typically look like this:
( firstAttribs, secondAttribs ) => { ...firstAttribs, text: firstAttribs.text + secondAttribs.text }
Further reading
- Two blocks I found useful when I was researching this are the list-item block and the button block. They gave me solid examples of using all three functions in the real world.
- You might want to see where
onSplit
andonReplace
are called in context. Check out how rich-text/split-value.js calls `onSplit` and how rich-text/index.js callsonMerge
. - And you’ll find that most of the logic from the default
onMerge
prop that is passed to blocks is found inside mergeBlocks.
With thanks to @bph and @marybaum for making this much more readable
Leave a Reply