InnerBlocks breaks Flexbox and CSS Grid styles

The question:

When using InnerBlocks so you can nest parent/child blocks you end up with additional HTML (in the editor) wrapping the child elements like so:

<div class="PARENT">
   <div class="block-editor-inner-blocks">
      <div class="block-editor-block-list__layout">
         <div class="block-editor-block-list__block wp-block is-selected">
            <div class="CHILD"></div>
         </div>
      </div>
   </div>
</div>

This causes a bit of a mess if using things like flexbox or grid on the parent element because it means the styles will instead apply to the .block-editor-inner-blocks instead.

e.g.

.PARENT {
   display: grid;
   grid-template-columns: repeat(3, minmax(0, 33%));
}

To get around this I’ve found myself having to do hacky overrides or write two different sets of CSS to handle the extra HTML that wraps the blocks in the editor:

.PARENT,
.PARENT .block-editor-block-list__layout {
   display: grid;
   grid-template-columns: repeat(3, minmax(0, 33%));
}
.editor-styles-wrapper .PARENT {
   display: block; /* remove grid in editor */
}

Is there a solution to avoid this? Or are you stuck with two sets of CSS or overrides when viewed inside the editor?

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

You can eliminate the wrapping containers abandoning InnerBlocks and instead using the useInnerBlocksProps hook which is how the blocks that come with core do it. This will allow your block markup to match the frontend without the editor wrapping things in additional tags.

If we look at the official buttons block:

import {
    BlockControls,
    useBlockProps,
    __experimentalUseInnerBlocksProps as useInnerBlocksProps,
    JustifyContentControl,
} from '@wordpress/block-editor';
...

function ButtonsEdit( {
    attributes: { contentJustification, orientation },
    setAttributes,
} ) {
    const blockProps = useBlockProps( {
        className: classnames( {
            [ `is-content-justification-${ contentJustification }` ]: contentJustification,
            'is-vertical': orientation === 'vertical',
        } ),
    } );
    const innerBlocksProps = useInnerBlocksProps( blockProps, {
        allowedBlocks: ALLOWED_BLOCKS,
        template: BUTTONS_TEMPLATE,
        orientation,
        __experimentalLayout: LAYOUT,
        templateInsertUpdatesSelection: true,
    } );
...
    return (
        <>
...
            <div { ...innerBlocksProps } />
        </>
    );
}

https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/buttons/edit.js

The blocks edit component eliminates the tags that surround the buttons block using useBlockProps, then passes the result to useInnerBlocksProps to do the same for its children. It also passes the parameters that it would have given to the InnerBlocks component as a second parameter.

This way it has full control over the markup of the block while still giving the editor the opportunity to add event handlers and other things.

Not that to use this, you must declare you use the v2 API when registering the block. If you don’t then useBlockProps won’t work

"apiVersion": 2,

A more minimal example:

import {
    useBlockProps,
    __experimentalUseInnerBlocksProps as useInnerBlocksProps,
} from '@wordpress/block-editor';

const Edit = ( props ) => {
    const blockProps = useBlockProps( {} );
    const innerBlocksProps = useInnerBlocksProps( blockProps, {} );
    return <div { ...innerBlocksProps } />;
};

This also has the benefit of avoiding a common bug in block validation where the innerblocks component shares a tag with another element, and the whitespace generation causes validation to fail, which isn’t possible here


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment