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.