From Shortcode to Block: Modernizing Legacy Features in WordPress
From Shortcode to Block: Modernizing Legacy Features in WordPress
Shortcodes have powered dynamic content in WordPress for over a decade, but as the Block Editor (Gutenberg) matures, modern development is shifting toward blocks. Blocks provide a visual, user-friendly editing experience that shortcodes can’t match. In this article, you’ll learn how to migrate your legacy shortcodes into reusable, editor-native blocks — without losing backward compatibility.
Why Move from Shortcodes to Blocks?
Shortcodes are powerful but invisible to users. They clutter the editor with code-like tags and make it hard to see how content will look on the front end. Blocks solve this by offering live previews, field controls, and standardized APIs.
Benefits of Converting Shortcodes to Blocks
- Visual editing: Users can see the final layout inside the editor.
- Structured input: Blocks use fields instead of shortcode attributes.
- Reusable logic: Register blocks and render them dynamically with PHP or JavaScript.
- Future-proof: Aligns with WordPress’s long-term direction (Gutenberg-first development).
Step 1: Identify a Legacy Shortcode
Let’s assume you already have a shortcode like this:
<?php
// functions.php
add_shortcode( 'button', function ( $atts, $content = null ) {
$atts = shortcode_atts( array(
'url' => '#',
'color' => 'blue',
), $atts, 'button' );
$url = esc_url( $atts['url'] );
$color = esc_attr( $atts['color'] );
$text = wp_kses_post( $content );
return '<a href="' . $url . '" class="btn btn-' . $color . '">' . $text . '</a>';
} );
Usage example in the Classic Editor:
[button url="https://example.com" color="green"]Buy Now[/button]
Step 2: Register a Block that Uses the Same Logic
You can modernize this shortcode into a block while reusing the same rendering callback. WordPress provides register_block_type() for dynamic (server-rendered) blocks.
<?php
// functions.php
add_action( 'init', function () {
register_block_type( 'wpt/button', array(
'api_version' => 2,
'title' => __( 'Button', 'your-textdomain' ),
'icon' => 'button',
'category' => 'design',
'attributes' => array(
'url' => array(
'type' => 'string',
'default' => '#',
),
'color' => array(
'type' => 'string',
'default' => 'blue',
),
'text' => array(
'type' => 'string',
'default' => 'Click Me',
),
),
'render_callback' => 'wpt_render_button_block',
) );
} );
function wpt_render_button_block( $attributes ) {
$url = esc_url( $attributes['url'] );
$color = esc_attr( $attributes['color'] );
$text = esc_html( $attributes['text'] );
return '<a href="' . $url . '" class="btn btn-' . $color . '">' . $text . '</a>';
}
This approach ensures you can update styling or logic in one place and apply it to both shortcode and block versions.
Step 3: Create a Block Editor Script (Optional Visual Controls)
For richer block controls, enqueue a JavaScript file that defines the block’s editor UI. Example using registerBlockType from @wordpress/blocks:
// file: /blocks/button/edit.js
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { TextControl, SelectControl, URLInputButton } from '@wordpress/components';
export default function Edit( { attributes, setAttributes } ) {
const { url, color, text } = attributes;
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<TextControl
label={ __( 'Button Text', 'your-textdomain' ) }
value={ text }
onChange={ ( value ) => setAttributes( { text: value } ) }
/>
<URLInputButton
label={ __( 'Button Link', 'your-textdomain' ) }
url={ url }
onChange={ ( value ) => setAttributes( { url: value } ) }
/>
<SelectControl
label={ __( 'Button Color', 'your-textdomain' ) }
value={ color }
options={[
{ label: 'Blue', value: 'blue' },
{ label: 'Green', value: 'green' },
{ label: 'Red', value: 'red' },
]}
onChange={ ( value ) => setAttributes( { color: value } ) }
/>
</div>
);
}
Then register this editor script in PHP:
<?php
add_action( 'init', function () {
wp_register_script(
'wpt-button-block-editor',
get_template_directory_uri() . '/blocks/button/edit.js',
array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components' ),
'1.0.0',
true
);
register_block_type( 'wpt/button', array(
'editor_script' => 'wpt-button-block-editor',
'render_callback' => 'wpt_render_button_block',
'attributes' => array(
'url' => array( 'type' => 'string', 'default' => '#' ),
'color' => array( 'type' => 'string', 'default' => 'blue' ),
'text' => array( 'type' => 'string', 'default' => 'Click Me' ),
),
) );
} );
Step 4: Maintain Backward Compatibility
If your site already has old shortcodes like [button], you can keep them working by simply reusing the same rendering function for both systems:
<?php
add_shortcode( 'button', function ( $atts, $content = null ) {
$atts = shortcode_atts( array(
'url' => '#',
'color' => 'blue',
), $atts, 'button' );
// Reuse the same logic as the block renderer
return wpt_render_button_block( array(
'url' => $atts['url'],
'color' => $atts['color'],
'text' => $content ?: 'Click Me',
) );
} );
Step 5: Test in the Editor
- Search for your new “Button” block in the editor.
- Set link, color, and label values from the sidebar.
- Save and verify the front-end matches shortcode output.
When to Use Dynamic vs Static Blocks
- Dynamic Blocks: PHP-rendered blocks that use
render_callback— best for data that may change (like shortcodes, queries, or user info). - Static Blocks: Fully saved as HTML — best for simple markup and design-based components.
Tips for Migrating Shortcodes
- Keep your shortcode active while introducing the block to avoid breaking old content.
- Match attribute names between shortcodes and block attributes for easier migration.
- Use
wp.blocks.registerBlockType()orblock.jsonto define metadata consistently. - Gradually phase out shortcode documentation once the block version is stable.
Conclusion
Converting shortcodes into modern WordPress blocks lets you preserve legacy logic while improving user experience. Start with a dynamic block using register_block_type() and reuse your shortcode’s render function. You’ll gain visual editing, structured attributes, and a future-proof way to manage content in the Block Editor.
Shortcodes belong to the past — blocks are the future of WordPress development.
🎨 Want to learn more? Visit our WordPress Customization Hub for tips and advanced techniques.