DEV Community

Andreas B. Vestergaard
Andreas B. Vestergaard

Posted on

How to make a spacing component for Gutenberg Blocks

One of the challenges we faced while building custom Gutenberg blocks was providing clients with an intuitive way to control spacing. Clients often need to adjust the spacing above or below blocks to maintain visual hierarchy and improve readability. To solve this problem, we developed a reusable SpacingSettings component.
The Problem

Before creating this component, we had to implement spacing controls for each block individually, leading to:

  • Duplicated code across multiple blocks
  • Inconsistent UI for spacing controls
  • Maintenance headaches when changes were needed

Our Solution

We created a reusable SpacingSettings component that can be easily imported and used across all our custom blocks. This component provides toggle controls for spacing at the top and bottom of blocks.

The core of our solution is a simple React component that leverages WordPress's built-in UI components. The component accepts properties for current spacing values, a change handler, and customizable labels.

const {
  element: { Fragment },
  components: { ToggleControl }
} = wp

const SpacingSettings = ({ onChange, spacingTop, spacingBottom, labels }) => {
  return (
    <Fragment>
      {typeof spacingTop !== 'undefined' && (
        <ToggleControl
          label={labels?.spacingTop?.label || 'Spacing Top'}
          help={spacingTop ? labels?.spacingTop?.enabled || 'Block has spacing top.' : labels?.spacingTop?.disabled || 'Block does not have spacing top.'}
          checked={spacingTop}
          onChange={(value) => onChange('spacingTop', value)}
        />
      )}
      {typeof spacingBottom !== 'undefined' && (
        <ToggleControl
          label={labels?.spacingBottom?.label || 'Spacing Bottom'}
          help={spacingBottom ? labels?.spacingBottom?.enabled || 'Block has spacing bottom.' : labels?.spacingBottom?.disabled || 'Block does not have spacing bottom.'}
          checked={spacingBottom}
          onChange={(value) => onChange('spacingBottom', value)}
        />
      )}
    </Fragment>
  )
}

export default SpacingSettings
Enter fullscreen mode Exit fullscreen mode

Let's break down what this component does:

  • It imports the necessary WordPress components (Fragment and ToggleControl)
  • It conditionally renders spacing controls based on provided props
  • It handles the toggling of spacing options and communicates changes back to the parent block
  • It supports customizable labels with sensible defaults

How to Use It

Implementing the component in any block is straightforward. You simply import it and include it in your block's Inspector Controls panel.

The component requires just a few props:

  • spacingTop and spacingBottom: Boolean values that determine the current state
  • onChange: A function that updates the block attributes when spacing is toggled
  • labels: An optional object for customizing the text displayed in the UI

Making It Work in the Frontend

The toggle controls are only half the solution. We add CSS classes based on the toggle states to make the spacing affect the block's appearance.

In our block's save function, we use the classnames utility to apply spacing classes conditionally:

const blockClasses = classnames(
  'my-custom-block',
  {
    'has-spacing-top': spacingTop,
    'has-spacing-bottom': spacingBottom
  }
);
Enter fullscreen mode Exit fullscreen mode

Then in our stylesheet, we define what those spacing classes actually do:

.has-spacing-top {
  margin-top: 2rem;
}

.has-spacing-bottom {
  margin-bottom: 2rem;
}
Enter fullscreen mode Exit fullscreen mode

Benefits

This component has significantly improved our development workflow:

  • DRY Code: We eliminated repetitive code across our block library
  • Consistent UI: All blocks now have the same spacing controls
  • Easier Maintenance: Changes to spacing controls only need to be made in one place
  • Client Satisfaction: Clients appreciate the intuitive controls for adjusting layout spacing

Lessons Learned

Creating reusable components for common functionality is a powerful pattern in Gutenberg development. It not only makes your codebase more maintainable but also creates a more consistent experience for content editors.

We've since applied this same pattern to other common block settings like background colors, animations, and responsive visibility options. Each reusable component has further streamlined our development process.

By investing time in building these foundation components, we've been able to develop new blocks much faster while maintaining a consistent editing experience across our entire block library.

Top comments (0)