WordPress.org

WordPress Developer Blog

How to add content-only editing support to a block

How to add content-only editing support to a block

WordPress 6.7 introduces many exciting features, but one subtle enhancement worth noting is the stabilization of content-only editing, now ready for use by extenders and block developers.

Although content-only editing was first introduced in WordPress 6.1, supporting this mode in custom blocks previously required an experimental property in block.json. Combined with the absence of an editor interface for applying content-only mode, it has remained one of the more overlooked and underutilized features of the block editor. But that might be changing soon.

So, if this is the first you’ve heard about content-only editing, don’t worry—you are not alone.

In this article, I’ll explain what content-only editing is, why it’s important to support it in your blocks, and how to implement it using a practical example.

What is content-only editing?

Content-only editing was added to WordPress two years ago as an additional locking mechanism designed to reduce the need to create custom blocks for layouts that could alternatively be built using block patterns.

The main advantage of using a custom block is that it gives you complete control over what users can edit, such as restricting them to only editing text, images, and other content elements. This simplifies the editing experience for users while maintaining a consistent layout and enforcing design standards. 

Content-only editing provides similar functionality to all blocks natively.

You enable it by adding templateLock: contentOnly to a container block, like Group, Column, and Cover. Once active, users can only edit specific elements of the nested content. Blocks without editable content are hidden from the List View and cannot be selected. Additionally, the Settings Sidebar for all blocks is disabled.

While often applied to blocks and patterns, you can also use content-only editing to lock entire page templates. For more information, refer to the Block Templates documentation. 

To demonstrate how this works, consider the following block layout, which features a Group block containing a Heading, Paragraph, and Social Icons block. Block styles and settings have been applied, and this example uses the Twenty Twenty-Five theme coming in WordPress 6.7.

In WordPress’s default editing mode, users can change anything they want about this design. Here’s the block markup for reference.

<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|50","bottom":"var:preset|spacing|50","left":"var:preset|spacing|50","right":"var:preset|spacing|50"},"margin":{"top":"var:preset|spacing|70","bottom":"var:preset|spacing|70"}}},"backgroundColor":"accent-5","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-accent-5-background-color has-background" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70);padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
    <!-- wp:heading -->
    <h2 class="wp-block-heading">Speaker Name</h2>
    <!-- /wp:heading -->
    
    <!-- wp:paragraph -->
    <p>Speaker Description—Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus urna sed urna ultricies ac tempor dui sagittis.</p>
    <!-- /wp:paragraph -->
    
    <!-- wp:social-links {"iconColor":"accent-5","iconColorValue":"#FBFAF3","iconBackgroundColor":"contrast","iconBackgroundColorValue":"#111111","showLabels":true,"size":"has-normal-icon-size","style":{"spacing":{"margin":{"top":"var:preset|spacing|50"}}}} -->
    <ul class="wp-block-social-links has-normal-icon-size has-visible-labels has-icon-color has-icon-background-color" style="margin-top:var(--wp--preset--spacing--50)">
        <!-- wp:social-link {"url":"#","service":"wordpress"} /-->
        <!-- wp:social-link {"url":"#","service":"chain"} /-->
        <!-- wp:social-link {"url":"#","service":"mail"} /-->
    </ul>
    <!-- /wp:social-links -->
</div>
<!-- /wp:group -->

Now, let’s apply the "templateLock": "contentOnly" attribute to the block markup of the Group block. You only need to modify the attribute object. The rest of the block markup should remain the same.

<!-- wp:group {"templateLock": "contentOnly","style":{"spacing":{"padding":{"top":"var:preset|spacing|50","bottom":"var:preset|spacing|50","left":"var:preset|spacing|50","right":"var:preset|spacing|50"}}},"backgroundColor":"accent-5","layout":{"type":"constrained"}} -->

To follow along, open a WordPress Playground instance running WordPress 6.7. Here’s a handy link

  1. Navigate to a new page.
  2. Open the Code Editor, and copy and paste the original block markup above.
  3. Add the "templateLock": "contentOnly" attribute to the Group block.
  4. Switch back to the Visual Editor. 

With those steps completed, you should see something like this.

If you compare the block layout in content-only mode to the default editing experience, you will quickly notice a few things:

  1. The List View is simplified and only displays the container (Group) and any internal blocks with editable content.
  2. The Settings Sidebar no longer displays block settings or styles, only the block style variations that are available to the container Group block.
  3. There is a new Content panel in the Settings Sidebar displaying the editable content blocks within the container.
  4. The icons in the Social Icons block are no longer editable. (Note this for later in the article)

Why support this editing mode?

Content-only editing is a powerful tool for creating curated and simplified user experiences. To make this feature effective, blocks must be designed to support it. For instance, as noted earlier, the Social Icons block currently does not function in this mode.

While future WordPress updates will address full support across core blocks, most sites include at least one custom block. For block developers, ensuring compatibility with content-only editing is essential. Prior to WordPress 6.7, enabling this feature in custom blocks required an experimental property in block.json called __experimentalRole. Now that the role property has been stabilized, adding support is generally straightforward.

You still might wonder why content-only editing is so important, especially since it has remained relatively niche until now. The answer lies in the new Write mode in Gutenberg and the increased emphasis on content-only editing outlined in the Design Editor Overview issue on GitHub.

These developments underline the future importance of content-only editing and its role in creating a simpler, more controlled editing experience within WordPress’s broader design tools. It’s time to get your blocks ready.

How to add content-only support to a block

Although I initially planned to create an example block plugin for this article, let’s instead examine the Social Link block in WordPress since it’s currently disabled in content-only mode as of WordPress 6.7. You can view the source code in the Gutenberg plugin.

The Social Link block is a child of the Social Icons (Social Links) block, which displays a row or column of icons that link to social profiles or websites. In many cases, you might want users to be able to modify link URLs in content-only mode. This is especially useful when a block layout is provided as a pattern that can be used in different contexts, such as the speaker profile block design shown earlier.

Here’s a quick demonstration of how these blocks work. Notice how toggling Show text displays a label for each icon.

For the remainder of this article, I will refer to the blocks by their technical names as used in the WordPress source code and block markup: wp:social-links and wp:social-link. Just remember that the Social Links block appears as the Social Icons block in the Editor, while the Social Link block is represented by the individual platform names such as WordPress, Facebook, LinkedIn, Link, Mail, etc.

So, let’s assume that the Social Link block is your custom block that needs content-only editing support. How do you go about implementing this?

Updating block.json

The first step is to update the block.json file of the Social Link block by adding the "role": "content" property to each attribute that should be editable when  "templateLock": "contentOnly" is set. 

For this example, the url and label attributes should be available. The url attribute holds the link for the social icon, while the label attribute represents an optional text label for the icon when Show text is enabled in the parent Social Links block.

The updated attributes section of the block.json file should look like this:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "core/social-link",
	"title": "Social Icon",
	"category": "widgets",
	"parent": [ "core/social-links" ],
	"description": "Display an icon linking to a social profile or site.",
	"textdomain": "default",
	"attributes": {
		"url": {
			"type": "string",
			"role": "content"
		},
		"service": {
			"type": "string"
		},
		"label": {
			"type": "string",
			"role": "content"
		},
		"rel": {
			"type": "string"
		}
	},
	...

If you are adding content-only editing to your own block and need to support older versions of WordPress, use __experimentalRole instead of role until you are ready to set the minimum WordPress version to 6.7. The only downside to this approach is a minor deprecation warning in the console.

With these changes, URL editing for social icons will be enabled automatically. This works because the url attribute is managed using the <SocialLinkURLPopover> component, which is part of the Editor canvas when the block is inserted. A block’s canvas elements become editable when at least one attribute is assigned the "role": "content" property.

You can see this demonstrated below.

The label attribute, on the other hand, is normally set using the Text field in the Settings Sidebar. The sidebar is disabled in content-only mode.

If you want a block attribute that is typically set in the sidebar to be editable in content-only mode, you need to create an alternative editing experience within the block. 

A great way to approach this is by taking inspiration from existing WordPress blocks. For example, the Image block allows users to edit the image’s alt text using an Alternative text dropdown in the block toolbar (source code).

Let’s apply a similar approach to the Social Link block.

Adding a custom editing experience

To begin, add a button to the block toolbar of the Social Link block by including content within a <BlockControls> component. For this, let’s follow the approach used in the Image block and implement a <Dropdown>  component. 

The following code should be placed within the return statement of the block’s Edit() function.

return (
	<>
		<BlockControls group="other">
			<Dropdown
				popoverProps={ { position: 'bottom right' } }
				renderToggle={ ( { isOpen, onToggle } ) => (
					<ToolbarButton
						onClick={ onToggle }
						aria-haspopup="true"
						aria-expanded={ isOpen }
					>
						{ __( 'Text' ) }
					</ToolbarButton>
				) }
				renderContent={ () => (
					<TextControl
						className="wp-block-social-link__toolbar_content_text"
						label={ __( 'Text' ) }
						help={ __( 'Provide a text label or use the default.' ) }
						value={ label }
						onChange={ ( value ) => setAttributes( { label: value } ) }
						placeholder={ socialLinkName }
					/>
				) }
			/>
		</BlockControls>
		<InspectorControls>
			...

The <Dropdown> component includes two key properties: renderToggle and renderContent

The renderToggle property is responsible for displaying the component that triggers the dropdown. In this case, a <ToolbarButton> is used since it’s designed to integrate seamlessly within block toolbars. 

The renderContent property specifies what is displayed when the dropdown opens. In this example, it contains a <TextControl> component used to set the label attribute. This <TextControl> is similar to the text input found in the block’s Settings Sidebar.

There are a few other details to note:

  • The <BlockControls> component includes the group="other" property, which ensures the Text button is positioned correctly within the toolbar.
  • The position of the dropdown is managed through the popoverProps property.
  • The <TextControl> component is assigned a custom class for styling purposes.

Once the code for the toolbar dropdown is added, the block will look like this.

You may have noticed two issues right away. First, the width of the dropdown needs to be fixed using CSS. Second, the Text button should not always be visible in the toolbar. 

When the block is not in content-only mode, users can already modify the label attribute from the Settings Sidebar. Additionally, if Show text is not enabled on the parent Social Links block, the button should not be displayed, regardless of the editing mode.

Applying the finishing touches

Let’s start with the easiest one. The dropdown width can be set by targeting the custom class added to the <TextControl> component. The following CSS should be added to the block’s editor.scss file.

.wp-block-social-link__toolbar_content_text {
	// Corresponds to the size of the text control input in the block inspector.
	width: 250px;
}

Next, you need to conditionally display the toolbar button based on the current editing mode and whether Show text is enabled.

The Social Link block already receives the showLabels value from the context provided by its parent, the Social Links block (source code). The editing mode can be determined using the useBlockEditingMode function.

Start by importing useBlockEditingMode from the @wordpress/block-editor package and defining the following constant in the block’s Edit() function:

const isContentOnlyMode = useBlockEditingMode() === 'contentOnly';

Now, wrap the <BlockControls> component created earlier in a conditional statement that checks for both isContentOnlyMode and showLabels.

return (
	<>
		{ isContentOnlyMode && showLabels && (
			<BlockControls group="other">
				...
			</BlockControls>
		) }
		<InspectorControls>
			...

The Text button in the toolbar will now display properly. When the Social Link block is in contentOnly mode, and Show text is enabled, users will be able to set both the icon link and the text label. 

Here’s a demonstration of the finished product.

You can find the full code for this example in the pull request that introduces content-only editing support for the Social Link block in the Gutenberg plugin. This functionality is expected to be included with WordPress 6.8 in early 2025.

Overall, the changes needed for the Social Link block were relatively minor. However, the modifications your custom blocks need will depend on their complexity and the amount of editing control you want to give users.

Closing thoughts

If you build custom blocks, consider whether some could be replaced with block patterns locked with content-only editing. If not, ensure your blocks support this mode. Although content-only editing is still somewhat niche, WordPress is expected to expand its use, and site builders using your blocks will come to expect it.

This is similar to adding official block supports so that your block’s styles and settings can be managed through theme.json and modified in Global Styles. Aligning your custom solutions with core functionality makes them more adaptable and robust.

As you explore content-only editing, feel free to share any questions or challenges you encounter during implementation in the comments.

Props to @greenshady, @fabiankaegy, and @get_dave for feedback and review on this post.

11 responses to “How to add content-only editing support to a block”

  1. NICHOLAS AMOL GOMES Avatar

    Thank you fot your support sharing me

  2. Alberto Prado Avatar

    It seems like an incredible tool for simplifying content creation, though it still feels somewhat more complex than creating a page filled with ACF fields.

    1. Shams Khan Avatar
      Shams Khan

      It is a long awaited, extremely essential feature but still not enough happy with the complexity of implementation. Hope in near version it will be simplified.

      1. Nick Diego Avatar

        I agree that it would be great if this was simpler to implement. Adding the role property to attributes is quite simple. The challenging portion is adapting your custom block’s functionality to the content-only editing, which will often be unique to the block. Since each implementation may be different depending on the block, simplification might be challenging. Do you have any suggestions for what you would like to see?

  3. RS Avatar
    RS

    Thank you! This is a great feature!
    If we are using ACF Pro to create custom blocks, does adding the “role”: “content” property to block.json file work?
    Does ACF Pro support the content-only editing feature?

    thanks!

    1. Nick Diego Avatar

      I am not sure how ACF Pro handles this feature. I recommend reaching out to ACF support.

  4. nicmare Avatar

    How about using cover block and i lock it but still want to be able to change the image source? is it possible to give access to media library only?

    1. Nick Diego Avatar

      Yeah, the Cover block in Core will need some updating to provide better content-only editing support.

  5. nicmare Avatar

    ive noticed when an image block is placed inside the cover block, i can change the image of the image block but not the image of the cover block itself. is this intended?

    1. Nick Diego Avatar

      This is something that will need to be updated in Core. It looks like the Cover block does not yet have full content-only editing support.

  6. Cathy Mitchell Avatar

    This is a whole philosophy of development: “Aligning your custom solutions with core functionality makes them more adaptable and robust.”

    I would add, “and extensible, and usable, and more in line with the open source culture”.

    Thank you for your work on this!

Leave a Reply

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