Theme.json layout and spacing options

In this lesson, you will learn about basic usage of the theme.json layout and spacing options, including content width, spacing and spacing units, and blockGap.
This lesson has been updated in preparation for WordPress version 6.3.

Prerequisites: Global Styles & theme.json.

Estimated reading time: 11 minutes

Last updated

The layout setting

The theme.json layout setting decides the width of the content and enables wide and full width blocks. This setting replaces add_theme_support( 'align-wide' );.
If you do not enable the layout setting in theme.json, the blocks are full width and aligned to the left in the block editor.

How to set content width using theme.json

The layout setting uses two key and value pairs:
contentSize sets the default width of the blocks in the editor and front.
wideSize enables the wide width option and sets the width for wide blocks.

  • You can use contentSize alone to allow full width but not wide width.
  • There is no theme.json setting for specifying a more precise width for the full width option.

If you include a value for the wideSize but not the contentSize, the editor will show the align none and align wide options, but the actual width of the block is the value placed in the wideSize setting.

Theme.json code example:

{
	"version": 2,
	"settings": {
		"layout": {
			"contentSize": "840px",
			"wideSize": "1100px"
		}
	}
}

Pixels are only one example; you can use any valid CSS width.

It is not possible to have different default content widths for different blocks or to disable wide and full widths for some blocks.

The setting uses a max-width combined with auto margins:

Default (called “None” in the editor):

.wp-container-id > * {
    max-width: 840px;
    margin-left: auto !important;
    margin-right: auto !important;
}

Wide:

.wp-container-id > .alignwide {
    max-width: 1100px;
}

Full:

.wp-container-id .alignfull {
    max-width: none;
}

The block editor layout controls

When you add the layout setting to theme.json, you enable the additional layout controls in the editor.

The following blocks use the inner block width control: Query loop, group, post content, cover, media & text, and column. However, different blocks also start with different default values.

A group block placed in the Site Editor will have the setting “Inner blocks use content width” enabled:

The updated toggle control for the layout setting is toggled on by default (checked).

While the post content block disables the setting:

The updated toggle control for the layout setting is toggled off in the post content block.

When Inner blocks use content width is toggled on:

  • The default width of the inner blocks is the value that your theme has assigned as the contentWidth in theme.json.
  • You can specify the width of the inner blocks using the input fields in the layout panel.
  • You can justify the blocks to the left, center, or right.

When Inner blocks use content width is toggled off:

  • The content width options in the layout panel are unavailable.
  • The inner blocks are always justified to the left (on ltr) and fill the width of the parent block.

Several other blocks support the justification controls in the layout panel, for example, the navigation and pagination blocks:

The layout panel has toggle options for justification, orientation, and for allowing the content to wrap to multiple lines.

Can I disable the layout width settings?

In WordPress 6.3, there is no way to completely disable these settings using theme.json.

  • ContentSize and wideSize can no longer be set to null or false, instead, it requires a string.
  • With the settings.layout removed from theme.json, the post content block shows an empty layout panel, while the group block still has the following layout settings:

Custom units

The default units for the width, height, and spacing controls vary depending on the block.
The navigation block and the social link blocks specify their units in the block.json file.
Other blocks get their default units from the UnitControl component.

The selectable units are px, em, rem, %, vw, and vh. You can add or remove units using settings.spacing.units:

{
	"version": 2,
	"settings": {
		"spacing": {
			"units": [ "px","em","rem"]
		}
	}
}

The setting replaces add_theme_support( 'custom-units', array() );.

If you only include one unit, the unit does not show in the control.

Margin and padding

How to enable margin and padding in theme.json

Margin and padding settings are disabled (false) by default. If you have enabled appearanceTools in the settings section of theme.json, you do not need to enable margin and padding separately.

You can enable margin and padding for all supported blocks under settings.spacing in theme.json:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	}
}

How to enable or disable padding and margin for individual block types:

{
	"version": 2,
	"settings": {
		"blocks": {
			"core/group": {
				"spacing": {
					"padding": false
				}
			},
			"core/site-title": {
				"spacing": {
					"padding": true,
					"margin": false
				}
			}
		}
	}
}

Which blocks support margins?

Spacing support improved greatly in WordPress 6.1 compared to earlier versions.
Please see the updated list in the block reference.

Which blocks support padding?

Please see the updated list in the block reference.

The dimensions panel

Some blocks display one or both spacing options by default. For other blocks, you need to toggle the option using the + or ellipsis button:

The dimension panel with the open options modal, where you can enable the padding and margin options.
Dimensions panel toggle
The dimension panel with padding and margin input fields enabled.
The reset all button is hidden inside the modal that can be opened with the "View options" menu.
Dimensions panel with options displaying. Some blocks only support top and bottom.

How to add default margin and padding to the <body> element

You can add a default margin and padding to the <body> element in the styles section of theme.json. using styles.spacing.margin and styles.spacing.padding:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	},
	"styles": {
		"spacing": {
			"margin": {
				"top": "0px",
				"right": "0px",
				"bottom": "60px",
				"left": "0px"
			},
			"padding": {
				"top": "100px",
				"right": "0px",
				"bottom": "0px",
				"left": "0px"
			}
		}
	}
}

CSS output:

body {
    margin-top: 0px;
    margin-right: 0px;
    margin-bottom: 60px;
    margin-left: 0px;
    padding-top: 100px;
    padding-right: 0px;
    padding-bottom: 0px;
    padding-left: 0px;
}

How to add default margin and padding to blocks

You can apply a default margin or padding to a block under styles.blocks in theme.json:

styles.blocks.blockname.spacing.margin
styles.blocks.blockname.spacing.padding

Examples:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	},
	"styles": {
		"blocks": {
			"core/button": {
				"spacing": {
					"padding": {
						"top": "0.3rem",
						"right": "1rem",
						"bottom": "0.3rem",
						"left": "1rem"
					}
				}
			},
			"core/social-links": {
				"spacing": {
					"margin": {
						"top": "0px",
						"bottom": "0px"
					}
				}
			},
			"core/latest-comments": {
				"spacing": {
					"padding": {
						"left": "0px"
					}
				}
			}
		}
	}
}

You can also add spacing to elements inside a block:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	},
	"styles": {
		"blocks": {
			"core/post-excerpt": {
				"elements": {
					"link": {
						"spacing": {
							"padding": {
							"top": "0.3rem",
							"bottom": "0.3rem"
							}
						}
					}
				}
			}
		}
	}
}

BlockGap

What is blockGap?

The blockGap setting in theme.json sets the vertical spacing between blocks by adjusting the margins depending on the position of the block:

  • The first block on the webpage and the first inner block or child block have no top or bottom margin.
  • The following blocks have a top margin corresponding to the value in the theme.json setting, and no bottom margin.

The block editor also uses blockGap for horizontal spacing between columns, gallery items, buttons, and social icons.

The default value for the CSS preset (--wp--style--block-gap) vary depending on the block, usually 2em for margins and 0.5em for gap.

The control for blockGap in the editor is called Block spacing, and you can find it in the dimensions panel:

The block spacing control in the Dimensions panel has an input field for a numeric value, and an option to select the unit.

How to enable blockGap

The blockGap settings are disabled (false) by default, meaning if you don’t want to use blockGap, you don’t need to do anything.
-If you have enabled appearanceTools in the settings section of theme.json, you do not need to enable blockGap separately.

{
	"version": 2,
	"settings": {
		"spacing": {
			"blockGap": true
		}
	}
}

How to add a default blockGap value

You can set a site-wide or block-specific value for blockGap inside the styles section in theme.json:

{
	"version": 2,
	"settings": {
		"spacing": {
			"blockGap": true
		}
	},
	"styles": {
		"spacing": {
			"blockGap": "1.5rem"
		}
	}
}

Can I add a blockGap value to a single block type?

Example:

{
	"version": 2,
	"settings": {
		"spacing": {
			"blockGap": true
		}
	},
	"styles": {
		"blocks": {
			"core/group": {
				"spacing": {
					"blockGap": "1.5rem"
				}
			}
		}
	}
}

Spacing presets

With spacing presets, both developers and users can select from predefined values for padding, margin and block spacing (block gap). The purpose is to make it easier to use consistent spacing throughout the website.

Presets are automatically enabled once you enable spacing in theme.json. If you have enabled any spacing setting like margin or padding, the spacing presets are also enabled.

The block editor displays the presets as a stepped slider:

The dimensions panel with three controls for padding, margin and block spacing.
The size of the spacing is adjusted with a stepped slider with a marker that can be dragged horizontally.

Spacing presets can be used with or without a scale. The default is to use a scale, but you can choose to disable the scale and use custom values.

How to use the default spacing scale

There are seven steps in the default scale (in addition to the first step, 0), and each step multiplies the value by 1.5. Each value is assigned to a CSS custom property:

CSS custom propertyValueEditor label
–wp–preset–spacing–200.44rem2X-Small
–wp–preset–spacing–300.67remX-Small
–wp–preset–spacing–401remSmall
–wp–preset–spacing–501.5remMedium
–wp–preset–spacing–602.25remLarge
–wp–preset–spacing–703.38remX-Large
–wp–preset–spacing–805.06rem2X-Large

In this theme.json example I have used the preset to add margin to the post title:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	},
	"styles": {
		"blocks": {
			"core/post-title": {
				"spacing": {
					"margin": {
					"bottom": "var(--wp--preset--spacing--50)",
					"top": "var(--wp--preset--spacing--30)"
					}
				}
			}
		}
	}
}

Inside HTML templates, it is recommended to add spacing presets to the block comment using pipe symbols:

<!-- wp:post-title {"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","right":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30"}}}} /-->

Note that the pipe symbol should not be used inside the block’s style attribute. Below is an example of a group block with padding on all sides:

<!-- wp:group {"layout":{"type":"constrained"},"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","right":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30"}}}} -->
<div class="wp-block-group" style="padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)"></div>
<!-- /wp:group -->

How to add your own custom spacing scale

You create your custom spacing scale under settings.spacing.spacingScale in theme.json.
The properties are:

  • operator: Use + to increase or * to multiply. The default is to multiply.
  • increment: A number. Each step in the scale is increased or multiplied with this value. The default value is 1.5.
  • steps: The number of steps to use in the scale. The default is 7.
  • mediumStep: A number. The medium value in the scale. The default value is 1.5.
  • unit: A string of valid CSS spacing units. The available values are: px, em, rem (default), vh, vw and %.

This example snippet autogenerates a scale based on the medium step.
This code is all you need if you want custom values, but you don’t feel a need to change the slugs or names (the visible name in the editor):

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true,
			"spacingScale": {
				"operator": "*",
				"increment": 1.5,
				"steps": 7,
				"mediumStep": 1.5,
				"unit": "rem"
			}
		}
	}
}

Keeping the default slugs and names makes your code more portable, and that way, you can easily modify the spacingScale in your theme’s style variations.

Using spacing preset values without a scale

There are several use cases for using presets without a scale. Perhaps the most common reason is to have values that scale, but needing to skip one or two steps and add a custom value.

To add custom sizes, you will use the spacingSizes setting with the attributes size, slug and name. The size can be any valid CSS size value, including clamp.

  • SpacingSizes replaces the default options in the spacing controls in the editor.
    • If you want to use the default presets but add a custom size to the scale, you must add all sizes to your theme.json since the theme.json setting overrides the default sizes.
  • The default CSS custom properties are still output for compatibility reasons, but are not visible in the control.
  • It is strongly recommended to use numeric slugs that range from the smallest to the largest size.

This example is copied from the theme Twenty Twenty-Three. This example is truncated, but notably, the smallest size, with the slug 20, is not being used:

"spacing": {
	"spacingSizes": [
		{
		"size": "clamp(1.5rem, 5vw, 2rem)",
		"slug": "30",
		"name": "1"
		},
		{
		"size": "clamp(1.8rem, 1.8rem + ((1vw - 0.48rem) * 2.885), 3rem)",
		"slug": "40",
		"name": "2"
		},
		{
		"size": "clamp(2.5rem, 8vw, 6.5rem)",
		"slug": "50",
		"name": "3"
		},
	]
}

The order in which you place the sizes is crucial. The control in the editor will display the sizes in the order you have added them in theme.json.

Do not do the following because “Hello” will show in between size one and two, even though it’s size value is larger. This would make the spacing controls more difficult to use:


"spacingSizes": [
    {
        "size": "1rem",
        "slug": "10",
        "name": "1"
    },
    {
        "size": "6rem",
        "slug": "hello",
        "name": "Hello"
    },
    {
        "size": "2rem",
        "slug": "20",
        "name": "2"
    }
],

Can I use different spacing presets for different blocks?

At this time, it is not possible to have different spacing presets for different block types.

How to disable custom spacing

By default, users can enter their own custom values and units in the spacing control:

Besides using the stepped slider, the spacing size values can also be manually entered into a numeric input field.

You can disable this ability by setting customSpacingSize to false:

{
	"version": 2,
	"settings": {
		"spacing": {
			"customSpacingSize": false
		}
	}
}

If you want to disable the default spacing scale, you can set the spacingScale steps to 0 using the following example:

{
	"version": 2,
	"settings": {
		"spacing": {
			"spacingScale": {
				"steps": 0
			}
		}
	}
}

Padding aware alignments

Since the introduction of full width blocks, theme developers have tried different ways to adjust the width of the content that is placed inside full width container blocks.

  • For example, when you used a full width group or columns block with paragraphs inside, the text would touch the edge of the browser window. -Unless you added padding or a background to each block.
  • Secondly, if we added a padding to the root level in the theme.json styles section (styles.spacing.padding), it meant that the blocks were no longer full width.

Gutenberg version 13.8 adds a new theme.json setting called useRootPaddingAwareAlignments that aims to solve this. The setting is opt-in. Enabling it adds four new CSS variables:
-wp--style--root--padding-top/right/bottom/left,
which uses the value from styles.spacing.padding:

{
	"version": 2,
	"settings": {
		"appearanceTools": true,
		"useRootPaddingAwareAlignments": true,
		"layout": {
			"contentSize": "840px",
			"wideSize": "1100px"
		}
	},
	"styles": {
		"spacing": {
			"padding": {
				"top": "1rem",
				"right": "1rem",
				"bottom": "1rem",
				"left": "1rem"
			}
		}
	}
}

You must use separate values for the padding; using a single value does not work.

If the value in settings.styles.spacing.padding is 1rem, WordPress adds 1rem padding to the body:

body {
    --wp--style--root--padding-top: 1rem;
    --wp--style--root--padding-right: 1rem;
    --wp--style--root--padding-bottom: 1rem;
    --wp--style--root--padding-left: 1rem;
}

Next, the setting adds a CSS class called .has-global-padding and calculates wether full width blocks should have a negative left or right margin. This margin compensates for the added padding.

<div class="has-global-padding is-layout-flow wp-container-2 wp-block-group alignfull">
.has-global-padding > .alignfull {
    margin-right: calc(var(--wp--style--root--padding-right) * -1);
    margin-left: calc(var(--wp--style--root--padding-left) * -1);
}

In my example theme, 1 rem is 16 pixels, and the computed margin for the full width block is:

margin-inline-end -16px
margin-inline-start -16px
margin-left -16px
margin-right -16px

Minimum height

Starting in WordPress version 6.2, the group and post content blocks support setting a minimum height via the dimensions panel. This theme.json setting is opt-in. You enable it by setting appearanceTools to true or with settings.dimensions.minHeight:

{
	"version": 2,
	"settings": {
		"dimensions": {
			"minHeight": true
		}
	}
}

This setting is separate from the cover block height control. Disabling this feature will not remove the cover block height control.

Position

The position setting is limited to the group block and to two options; default, and sticky:

The position panel of the group block.

Position is disabled by default and can be enabled by enabling appearanceTools or with:

{
	"version": 2,
	"settings": {
		"position": {
			"sticky": true
		}
	}
}

Resources

How to add a sticky header

Rich tabor has written a blog post about using custom spacing in block themes:

https://richtabor.com/standardizing-theme-json-spacing/