Adding Align Controls to a Gutenberg Block

Recently I created a section wrapper block, meant to house other blocks. I wanted the block itself to support wide and full alignment but also for the blocks within it to support wide or full layout, similar to the core Group block. (This block could also have been a block pattern using the Group block, but I wanted the ability to define preset Block Styles the editor could use.)

block.json

First, I enabled the layout in the block.json. Note that this is still an experimental feature and subject to change in the future.

"supports": {
    "align": ["wide", "full"],
    "__experimentalLayout": true
}

Block Edit

Next, within the block’s edit function, I needed to check that the block supported layouts and pass the __experimentalLayout prop on to the InnerBlocks. Much of this came from the core Group block.

Get the Setting

To check whether the block has support for layout enabled (although it’s in block.json, it could have been modified by another plugin or theme), I used useSelect to retrieve the settings from the block-editor store.

import { store } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';

function Edit({ clientId }) {
  const hasLayoutSupport = useSelect(
    (select) => {
      const { getSettings } = select(store);
      return getSettings()?.supportsLayout;
    },
    [clientId]
  );
}

Get the Default Layout

Next, I defined the default layout using useSetting to get the current layout setting from the editor, if defined. (The layout setting controls the max widths for wide and regular constrains).

import { useSetting } from '@wordpress/block-editor';
...
const defaultLayout = useSetting( 'layout' ) || {};

Get the Block’s Layout Setting

The block can inherit the default layout, define its own max widths, or do neither. To determine which, I retrieved the layout attribute. If the layout attribute exists and layout.inherit is true, the block is inheriting the default layout. Once I know what layout, if any, is in use, I can determine the type.

const { layout = {} } = attributes;
const layoutInUse = !!layout && layout.inherit 
  ? defaultLayout 
  : layout;
const { type = 'default' } = layoutInUse;

Determine if InnerBlocks Get Layout Support

With all that in place, I needed to enable layout support on the InnerBlocks if the block supports layout in its settings or if it has a custom layout currently in use. If it does, I needed to tell InnerBlocks what layout to use. If the block didn’t have a layout attribute but has layout support enabled, the default layout is used.

const layoutSupportEnabled = hasLayoutSupport || type !== 'default';
...
<InnerBlocks
  __experimentalLayout={
    layoutSupportEnabled ? layoutInUse : undefined
  }
/>

Altogether, minus the additional block template for the section block, it looks something like this:

import {
  useBlockProps,
  InnerBlocks,
  store,
  useSetting,
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';

function Edit({ attributes, clientId }) {
  const hasLayoutSupport = useSelect(
    (select) => {
      const { getSettings } = select(store);
      return getSettings()?.supportsLayout;
    },
    [clientId]
  );
  const defaultLayout = useSetting('layout') || {};
  const { layout = {} } = attributes;
  const layoutInUse = !!layout && layout.inherit 
    ? defaultLayout 
    : layout;
  const { type = 'default' } = layoutInUse;
  const layoutSupportEnabled 
    = hasLayoutSupport || type !== 'default';
  const blockProps = useBlockProps({
    className: classnames('l-section'),
  });
  return (
    <section {...blockProps}>
      <InnerBlocks
        __experimentalLayout={
          layoutSupportEnabled ? layoutInUse : undefined
        }
      />
    </section>
  );
}
References