
When updating Gutenberg blocks, maintaining Gutenberg Backward Compatibility ensures that existing content remains functional and visually consistent. This guide explains how to manage deprecated block versions, migrate attributes, and handle markup changes while preserving user content.
1. Why Gutenberg Backward Compatibility Matters
- User Trust: Prevents existing posts from breaking after plugin/theme updates.
- Smooth Transitions: Allows users to adopt new features at their own pace.
- Data Integrity: Ensures old content remains editable without data loss.
2. Using the deprecated Property
The deprecated array in registerBlockType() lets you define older versions of a block. The editor uses this to parse and render legacy content.
Basic Deprecation Example
// Js Code
registerBlockType('my-plugin/my-block', {
// Current version
attributes: { title: { type: 'string' } },
save: ({ attributes }) => <h2>{attributes.title}</h2>,
// Deprecated versions
deprecated: [
{
// Version 1: Old attributes and save function
attributes: { heading: { type: 'string' } },
save: ({ attributes }) => <h3>{attributes.heading}</h3>,
migrate: (attributes) => ({ title: attributes.heading }),
},
],
});
Explanation:
- attributes: Old schema for attributes.
- save: Original markup function.
- migrate: Converts old attributes to the new schema when the block is resaved.
3. Attribute Migration
When renaming or restructuring attributes, use migration functions to transform old data.
Example: Renaming an Attribute:
// JS Code
deprecated: [
{
attributes: { oldTitle: { type: 'string' } },
save: ({ attributes }) => <h3>{attributes.oldTitle}</h3>,
migrate: (attributes) => ({
title: attributes.oldTitle, // Map oldTitle to title
}),
},
]
Example: Changing Data Types
// Js Code
// New: Store title as an object with text and color
attributes: {
title: {
type: 'object',
default: { text: '', color: 'black' },
},
},
// Old: Title was a string
deprecated: [
{
attributes: { title: { type: 'string' } },
save: ({ attributes }) => <h3>{attributes.title}</h3>,
migrate: (attributes) => ({
title: { text: attributes.title, color: 'black' },
}),
},
]
4. Handling Markup Changes
If the block HTML structure changes, provide deprecated save functions to parse old content.
Example: Restructuring HTML
// Js Code
// Current save function
save: ({ attributes }) => (
<div className="new-container">
<h2>{attributes.title}</h2>
</div>
),
// Deprecated version with old markup
deprecated: [
{
save: ({ attributes }) => (
<section className="old-container">
<h3>{attributes.title}</h3>
</section>
),
// Optional: Transform old HTML to new structure
__experimentalConvert: (domNode) => {
const title = domNode.querySelector('h3').textContent;
return {
title,
// New attributes can be added here
};
},
},
]
5. Block Validation and Automatic Migration
WordPress automatically validates blocks by comparing saved markup to the current save() output. If invalid, it checks deprecated versions for a match.
Validation Workflow
- Parse saved content.
- Check against current save().
- If invalid, iterate through deprecated entries until a valid version is found.
- Use the deprecated save() to render the block.
- When the user again saves the post, the block upgrades to the latest version.
6. Advanced Migration with _experimentalConvert
For complex HTML changes, use _experimentalConvert to transform old markup into new attributes.
Example: Extracting Data from HTML
// Js Code
deprecated: [
{
save: ({ attributes }) => <div class="old-class">{attributes.text}</div>,
__experimentalConvert: (domNode) => {
const text = domNode.querySelector('.old-class').textContent;
return { text };
},
},
]
7. Maintaining Multiple Deprecated Versions
List deprecations in reverse chronological order. The editor checks them sequentially.
// JS Code
deprecated: [
// Version 2 (recent)
{
attributes: { /* ... */ },
save: () => /* ... */,
migrate: (attrs) => /* ... */,
},
// Version 1 (oldest)
{
attributes: { /* ... */ },
save: () => /* ... */,
},
]
8. Testing Backward Compatibility
Manual Testing
- Create a post with an old block version.
- Update your plugin/theme.
- Verify the block renders correctly.
- Edit and resave the post to trigger migration.
Automated Tests
Use Jest and the @wordpress/block-editor package to test migrations:
// Js Code
import { createBlock } from '@wordpress/blocks';
test('migrates old attributes', () => {
const oldBlock = createBlock('my-plugin/my-block', { oldTitle: 'Hello' });
const migratedBlock = migrate(oldBlock);
expect(migratedBlock.attributes.title).toBe('Hello');
});
9. Best Practices
- Document Changes: Track deprecated versions in your codebase.
- Gradual Deprecation: Remove old versions only after ensuring most content has migrated.
- Use Version Constants: Manage versions programmatically:
// Js Code
const BLOCK_VERSION = 3;
registerBlockType('my-plugin/my-block', {
attributes: { /* ... */ },
save: () => /* ... */,
deprecated: [
/* ... */
],
});
- Notify Users: Add admin notices for critical updates requiring action.
10. Real-World Example: Converting a Legacy Banner Block
Scenario: A banner block initially stored text as a string but now supports subtitles and styles.
Step 1: Update Attributes
// Js Code
attributes: {
content: {
type: 'object',
default: { main: '', subtitle: '', color: 'blue' },
},
},
Step 2: Deprecate the Old Version
// Js Code
deprecated: [
{
attributes: { text: { type: 'string' } },
save: ({ attributes }) => <div class="banner">{attributes.text}</div>,
migrate: (attributes) => ({
content: { main: attributes.text, subtitle: '', color: 'blue' },
}),
},
]
Step 3: Test Migration
- Old block: <div class=”banner”>Sale</div>
- Migrated attributes: { content: { main: ‘Sale’, subtitle: ”, color: ‘blue’ } }
Conclusion
Implementing backward compatibility in Gutenberg blocks requires detailed planning, testing measures, and explicit documentation. Your block will enable user-friendly transitions by using the deprecated property with migration functions and validation workflows. This Process guarantee data reliability and user satisfaction while keeping trust and reliability as top priorities.
About the writer
Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developer understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.