How contentOnly editing works for patterns and blocks in WordPress 7.0

By Ian Svoboda on May 27, 2026

WordPress 7.0 makes contentOnly mode the default for unsynced patterns, which changes how editors interact with anything you build. Here’s what that means, how to make your blocks play nicely, and how to opt out if it’s not for you.

WordPress 7.0 flips a default that quietly changes how everyone interacts with patterns. Unsynced patterns and template parts now land in contentOnly mode the moment you insert them. That sounds small. In practice it changes what your editors can click, what shows up in List View, and what your custom (or third-party) blocks need to do to stay editable.

I’ll be honest: the editing flow this introduces isn’t particularly intuitive. The point of this post isn’t to defend it or sell you on it (I’m not exactly a fan of it myself). The point is to understand how it actually works so you can build with it instead of against it.

What contentOnly actually does to a pattern

When a pattern is in contentOnly mode, the editor only lets you edit the basic content of a block such as text, images, link targets, that kind of thing. Block structure and design controls get hidden away. List View flattens. Blocks that don’t expose any content attributes become non-selectable, like they’re behind glass.

You can still see the rendered pattern. You just can’t drill into its internals from the canvas, which is the whole point.

The design intention of this feature was to make it easier to make content specific edits without accidentally changing the design or layout of a given section. If you’re making a site for clients or anyone is not a developer, this could be beneficial to them by hiding away controls that they (and you) don’t want them to use in the first place.

The contentOnly mode itself has been around since 6.7. In WordPress 7.0, contentOnly mode is now the default for unsynced patterns. Anyone used to working with these before version 7 who isn’t familiar with these changes may find this behavior unexpected (or even confusing).

Editing a pattern’s structure (the part that feels weird)

How do you actually change a pattern’s layout or styling now? There are two flows, depending on the pattern type.

For unsynced patterns, you click an “Edit pattern” button (or double-click the body of the pattern) and the editor drops you into spotlight mode. From there you have full editing access, scoped to that pattern instance. Changes stay local.

For synced patterns and template parts, you click “Edit original” instead. That opens the source in an isolated editor with navigation back to the document you came from. Changes apply globally, which is the whole point of synced.

Double-clicking to enter spotlight mode is discoverable in theory and confusing in practice. If you’ve handed a site off to a client and haven’t explained this to them, you can probably expect a confused email.

If you’re leaving this functionality enabled, it’s worth a short loom video or a line in your handoff docs explaining the difference between “I want to edit the text” (just click) and “I want to change the layout” (double-click or use the button in the toolbar).

Setting up your block’s attributes for contentOnly editing

When your block sits inside a contentOnly pattern, the editor needs to know which of its attributes count as “content” so it can let users edit those (and only those). You signal this in block.json by adding "role": "content" to the relevant attributes.

block.json
{
  "attributes": {
    "url": {
      "type": "string",
      "role": "content"
    },
    "label": {
      "type": "string",
      "role": "content"
    }
  }
}

If a block has no attributes marked with "role": "content", the editor treats it as a non-content block which hides it in the block list and prevents you from selecting it. That’s a sharp behavior change if your users were used to clicking around freely.

This functionality was more niche and focused around partially synced patterns or even template parts. But now it applies to any unsynced pattern out of the box.

If you’re writing custom blocks, check them and mark the attributes that represent the content users should be editing (text, URLs, labels, image IDs, etc.). Don’t worry about attributes for design, data, or other structure.

The supports flag: contentRole

Some blocks don’t have an obvious attribute to mark. Wrappers, dynamic blocks that pull all their content from external sources, layout-style containers where the whole block is “the content” but no single attribute represents it. For those, there’s a supports flag:

block.json
{
  "supports": {
    "contentRole": true
  }
}

That flips the entire block on inside contentOnly mode without you having to nominate a specific attribute.

Note
The official dev note for this feature shows "contentOnly": true in the code example at this spot, which appears to be a typo. The actual supports flag is contentRole: true. You can confirm by looking at the core block definitions, where contentRole shows up in supports for blocks like Query and the new Accordion block. If you copy-pasted from the dev note, your block is silently doing nothing.

The official guidance is to prefer "role": "content" on a specific attribute when you can. Use contentRole: true when there genuinely isn’t a sensible attribute to mark. Think of contentRole: true as a blanket switch that keeps the whole block editable in contentOnly mode, where role: 'content' is the more granular per-attribute version.

When you don’t control block.json

Sometimes you’re using custom blocks that you didn’t build such as GenerateBlocks, Kadence, etc. If the plugin author hasn’t shipped role: 'content' on the correct (or expected) attributes those blocks would not match the behavior of core blocks.

The workaround: filter the block’s registration args before they hit the registry. WordPress fires register_block_type_args for every block, and you can mutate $args in place.

PHP
<?php
function add_content_role_supports( $args, $block_type ) {
	if ( 'generateblocks/text' === $block_type ) {
		$args['attributes']['content']['role'] = 'content';
	}

	return $args;
}
add_filter( 'register_block_type_args', 'add_content_role_supports', 10, 2 );

Two details worth calling out:

  • $block_type is the full block name as a string (namespace/name), so you can target whichever block needs the patch.
  • We’re filtering the attribute definition the editor sees, not the block’s source files. The plugin can update independently and your patch keeps working as long as the attribute still exists. If the plugin is updated accordingly later you can just remove the above filter.

The same pattern works for any block you don’t own. Swap the block name and the attribute key for whatever you need. If you find yourself doing this for more than two or three blocks, drop the conditional and use a small array of block-to-attribute mappings.

Opting out for unsynced patterns

If contentOnly-by-default doesn’t fit your editorial workflow at all (your “patterns” are really starter layouts, your editors are technical, you have a strong opinion about this, etc), 7.0 ships a setting to turn it off:

PHP
<?php
add_filter( 'block_editor_settings_all', function( $settings ) {
	$settings['disableContentOnlyForUnsyncedPatterns'] = true;
	return $settings;
} );

Drop that in your theme’s functions.php or a small site plugin and unsynced patterns go back to full editing on insert.

One thing to keep in mind: this setting only affects unsynced patterns. Synced patterns (core/block) and template parts are unaffected and will still go into contentOnly mode. That’s by design. Those are shared resources where the guardrail genuinely makes sense. The setting is for the case where someone took a Figma layout, called it a “pattern”, and expected it to behave like a starter template.

There’s also a JavaScript version using wp.data.dispatch('core/block-editor').updateSettings() if you need to flip the setting client-side, but the PHP filter is the right move for almost every theme or plugin context.

Wrapping up

The three levers you have are:

  1. Mark your own block attributes with "role": "content" (or contentRole: true in supports if no attribute fits)
  2. Patch third-party blocks at registration with register_block_type_args
  3. Opt out at the site level with disableContentOnlyForUnsyncedPatterns if you don’t want the functionality at all

Happy coding!

Further Reading



Leave a Reply

Your email address will not be published. Required fields are marked *