Block Variations & Transforms
Block Variations
Variations let you create presets of a single block with different default attributes:
// src/variations.js
import { __ } from '@wordpress/i18n';
export const variations = [
{
name: 'testimonial-simple',
title: __( 'Simple Testimonial', 'theme-blocks' ),
description: __( 'A minimal testimonial without rating.', 'theme-blocks' ),
icon: 'format-quote',
attributes: {
showRating: false,
showAvatar: false,
style: 'minimal',
},
scope: [ 'inserter', 'block', 'transform' ],
isDefault: true,
},
{
name: 'testimonial-card',
title: __( 'Testimonial Card', 'theme-blocks' ),
description: __( 'Featured testimonial with avatar and rating.', 'theme-blocks' ),
icon: 'id-alt',
attributes: {
showRating: true,
showAvatar: true,
style: 'card',
},
scope: [ 'inserter', 'block', 'transform' ],
},
{
name: 'testimonial-featured',
title: __( 'Featured Quote', 'theme-blocks' ),
description: __( 'Large quote with decorative styling.', 'theme-blocks' ),
icon: 'megaphone',
attributes: {
showRating: true,
showAvatar: true,
style: 'featured',
align: 'wide',
},
scope: [ 'inserter' ],
},
];Register Variations
// src/index.js
import { registerBlockType, registerBlockVariation } from '@wordpress/blocks';
import metadata from './block.json';
import Edit from './edit';
import save from './save';
import { variations } from './variations';
registerBlockType( metadata.name, {
edit: Edit,
save,
variations,
} );Variation Scope
| Scope | Where It Appears |
|---|---|
inserter | Block inserter panel |
block | Block settings panel |
transform | Transform menu |
Core Block Variations
You can also create variations of core blocks:
// Create a custom "Hero" variation of the Cover block
registerBlockVariation( 'core/cover', {
name: 'hero-section',
title: __( 'Hero Section', 'theme-blocks' ),
description: __( 'A full-width hero with overlay.', 'theme-blocks' ),
attributes: {
align: 'full',
dimRatio: 50,
minHeight: 600,
contentPosition: 'center center',
},
innerBlocks: [
[ 'core/heading', { level: 1, placeholder: 'Hero Title' } ],
[ 'core/paragraph', { placeholder: 'Hero description...' } ],
[ 'core/buttons', {}, [
[ 'core/button', { text: 'Get Started' } ],
] ],
],
scope: [ 'inserter' ],
} );Block Transforms
Transforms let users convert between block types:
// src/index.js
import { createBlock } from '@wordpress/blocks';
registerBlockType( metadata.name, {
edit: Edit,
save,
transforms: {
from: [
// Transform FROM core quote
{
type: 'block',
blocks: [ 'core/quote' ],
transform: ( { value, citation } ) => {
return createBlock( 'theme/testimonial', {
content: value,
author: citation,
} );
},
},
// Transform FROM core paragraph
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) => {
return createBlock( 'theme/testimonial', {
content,
} );
},
},
// Transform FROM raw text (paste)
{
type: 'raw',
selector: 'blockquote',
schema: {
blockquote: {
children: {
p: { children: [] },
cite: { children: [] },
},
},
},
transform: ( node ) => {
const content = node.querySelector( 'p' )?.textContent || '';
const author = node.querySelector( 'cite' )?.textContent || '';
return createBlock( 'theme/testimonial', { content, author } );
},
},
],
to: [
// Transform TO core quote
{
type: 'block',
blocks: [ 'core/quote' ],
transform: ( { content, author } ) => {
return createBlock( 'core/quote', {
value: content,
citation: author,
} );
},
},
// Transform TO core paragraph
{
type: 'block',
blocks: [ 'core/paragraph' ],
transform: ( { content } ) => {
return createBlock( 'core/paragraph', {
content,
} );
},
},
],
},
} );Multi-Block Transforms
Transform multiple blocks at once:
transforms: {
from: [
{
type: 'block',
blocks: [ 'core/paragraph' ],
isMultiBlock: true,
transform: ( paragraphs ) => {
// Combine multiple paragraphs into one testimonial
const content = paragraphs
.map( p => p.content )
.join( '<br>' );
return createBlock( 'theme/testimonial', { content } );
},
},
],
}Transform Priority
Control transform order with priority (lower = higher priority):
{
type: 'block',
blocks: [ 'core/quote' ],
priority: 5, // Show earlier in transform menu
transform: ( attributes ) => { /* ... */ },
}isMatch Function
Conditionally allow transforms:
{
type: 'block',
blocks: [ 'core/paragraph' ],
isMatch: ( { content } ) => {
// Only transform if content looks like a quote
return content?.includes( '"' );
},
transform: ( attributes ) => { /* ... */ },
}Block Categories
Register a custom block category:
// In PHP
function theme_register_block_category( $categories ) {
return array_merge(
array(
array(
'slug' => 'theme-blocks',
'title' => __( 'Theme Blocks', 'theme-blocks' ),
'icon' => 'star-filled',
),
),
$categories
);
}
add_filter( 'block_categories_all', 'theme_register_block_category' );Then use in block.json:
{
"category": "theme-blocks"
}Next Steps
In the next lesson, we'll add frontend interactivity with View Scripts and the Interactivity API.
๐ฏ Lesson Complete! You can now create block variations and seamless transforms between blocks.