I have some custom blocks. Each has a save function that gets content from the block's attributes:

( { className, attributes } ) => (
    <div className = { className }>{ attributes.content }</div>

Here is the configuration of the content attribute:

attributes: {
    content: {
        source: "html",
        type: "string",
        selector: "div"

The content is an object, namely, a React component. The content renders as expected in the block editor. When I preview the post, it renders as expected on the front end—likewise when I view it after publishing.

But when I go to edit the saved post, block validation fails. The error message shows that the "Content generated by 'save' function" escapes the < in the HTML content from attributes, whereas the "Content retrieved from post body" does not.

Say the retrieved content is this:

<div class="wp-block-example-class"><p>Example paragraph</p></div>

Then the saved content is this:

<div class="wp-block-example-class">&lt;p>Example paragraph&lt;/p></div>

I found this support post about a similar problem solved by using RawHTML. So I tried changing my save function:

( { className, attributes } ) => (
    <div className = { className }>
        <RawHTML>{ renderToString( attributes.content) }</RawHTML>

I got the same results described above. Note that I first tried using RawHTML without renderToString—that is, <RawHTML>{ attributes.content }</RawHTML>. The content rendered as expected in the editor but not on the front end, where the block's div wrapper contained the text "undefined" and "null" where elements derived from React components should have been.

In case it matters, the blocks are created and inserted programmatically. Simplified, here is the process:

const myContent = ( <MyComponent>{"Some content"}</MyComponent> );
const myBlock   = createBlock( "example/my-block", {  
    content: myContent 
} );

dispatch( "core/block-editor" ).insertBlock( myBlock );

What might I try to prevent < from being escaped by the block's save function?

1 Answer 1


I solved the problem by rendering my block's content, a component, to a string and making that string the value of a RichText component in my block's save function.

To make my component a string, I used renderToString:

// Create the content and the block
const myContent = ( <MyComponent>{"Some content"}</MyComponent> );
const myBlock   = createBlock( "example/my-block", {  
    content: renderToString( myContent )
} );

And here is my block's save function:

( { className, attributes } ) => (
        tagName   = 'div'
        className = { className }
        value     = { attributes.content }

I tried this solution since I have other custom blocks that use RichText, and those blocks weren't having an issue with unwanted escaping. But I don't know why I needed to use RichText for the blocks that were having an issue. The content of the affected blocks is generated automatically and contains no formats, core or custom.

One last note. The "undefined" and "null" that appeared in my block's wrapper on the front end when using <RawHTML>{ attributes.content }</RawHTML> in my block's save function resulted from a front end script, not anything related to the block editor. My block's save function was actually generating nothing inside the block wrapper.

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.