LESSON 7 โฑ๏ธ 12 min read

Adding Dynamic Functionality

Block Themes & PHP

Block themes can still use functions.php for:

  • Custom functionality
  • Block registration
  • Action/filter hooks
  • External integrations

Setting Up functions.php

get( 'Version' )
    );
    
    // JavaScript
    wp_enqueue_script(
        'my-theme-scripts',
        get_template_directory_uri() . '/assets/js/main.js',
        array(),
        wp_get_theme()->get( 'Version' ),
        true
    );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_assets' );

/**
 * Enqueue editor assets
 */
function my_theme_enqueue_editor_assets() {
    wp_enqueue_style(
        'my-theme-editor',
        get_template_directory_uri() . '/assets/css/editor.css',
        array(),
        wp_get_theme()->get( 'Version' )
    );
}
add_action( 'enqueue_block_editor_assets', 'my_theme_enqueue_editor_assets' );

Custom Block Styles

/**
 * Register custom block styles
 */
function my_theme_register_block_styles() {
    register_block_style(
        'core/button',
        array(
            'name'  => 'gradient',
            'label' => __( 'Gradient', 'my-theme' ),
        )
    );
    
    register_block_style(
        'core/image',
        array(
            'name'  => 'shadow',
            'label' => __( 'With Shadow', 'my-theme' ),
        )
    );
    
    register_block_style(
        'core/group',
        array(
            'name'  => 'card',
            'label' => __( 'Card', 'my-theme' ),
        )
    );
}
add_action( 'init', 'my_theme_register_block_styles' );

Style them in CSS:

/* Button gradient style */
.wp-block-button.is-style-gradient .wp-block-button__link {
    background: linear-gradient(135deg, var(--wp--preset--color--primary) 0%, var(--wp--preset--color--secondary) 100%);
}

/* Image shadow style */
.wp-block-image.is-style-shadow img {
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
}

/* Group card style */
.wp-block-group.is-style-card {
    padding: var(--wp--preset--spacing--30);
    background: var(--wp--preset--color--light);
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

Block Variations

/**
 * Register block variations
 */
function my_theme_register_block_variations() {
    wp_enqueue_script(
        'my-theme-block-variations',
        get_template_directory_uri() . '/assets/js/block-variations.js',
        array( 'wp-blocks', 'wp-dom-ready' ),
        wp_get_theme()->get( 'Version' ),
        true
    );
}
add_action( 'enqueue_block_editor_assets', 'my_theme_register_block_variations' );
// assets/js/block-variations.js
wp.domReady(() => {
    wp.blocks.registerBlockVariation('core/columns', {
        name: 'three-equal',
        title: 'Three Equal Columns',
        icon: 'columns',
        scope: ['inserter'],
        innerBlocks: [
            ['core/column', { width: '33.33%' }],
            ['core/column', { width: '33.33%' }],
            ['core/column', { width: '33.33%' }],
        ],
    });
    
    wp.blocks.registerBlockVariation('core/group', {
        name: 'section',
        title: 'Section',
        icon: 'align-wide',
        scope: ['inserter'],
        attributes: {
            align: 'full',
            style: {
                spacing: {
                    padding: {
                        top: 'var:preset|spacing|50',
                        bottom: 'var:preset|spacing|50',
                    }
                }
            }
        },
    });
});

Custom Render Callbacks

Add dynamic content to templates:

/**
 * Register dynamic block
 */
function my_theme_register_dynamic_blocks() {
    register_block_type( 'my-theme/current-year', array(
        'render_callback' => 'my_theme_render_current_year',
    ) );
}
add_action( 'init', 'my_theme_register_dynamic_blocks' );

function my_theme_render_current_year( $attributes, $content ) {
    return '<span class="current-year">' . date( 'Y' ) . '</span>';
}

Use in templates:

<!-- wp:paragraph -->
<p>ยฉ <!-- wp:my-theme/current-year /--> Your Company. All rights reserved.</p>
<!-- /wp:paragraph -->

Block Bindings API (WP 6.5+)

Connect block attributes to dynamic data:

/**
 * Register custom block bindings
 */
add_action( 'init', function() {
    register_block_bindings_source( 'my-theme/meta', array(
        'label' => __( 'Post Meta', 'my-theme' ),
        'get_value_callback' => function( $source_args, $block_instance ) {
            if ( empty( $source_args['key'] ) ) {
                return '';
            }
            return get_post_meta( get_the_ID(), $source_args['key'], true );
        },
    ) );
} );

Use in templates:

<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"my-theme/meta","args":{"key":"subtitle"}}}}} -->
<p></p>
<!-- /wp:paragraph -->

Theme Hooks

/**
 * Before header
 */
function my_theme_before_header() {
    // Add announcement bar, skip links, etc.
    echo '<a class="skip-link" href="#main">' . __( 'Skip to content', 'my-theme' ) . '</a>';
}
add_action( 'wp_body_open', 'my_theme_before_header' );

/**
 * Modify block content
 */
function my_theme_modify_block_content( $block_content, $block ) {
    if ( 'core/image' === $block['blockName'] ) {
        // Add lazy loading attributes, etc.
    }
    return $block_content;
}
add_filter( 'render_block', 'my_theme_modify_block_content', 10, 2 );

Next Steps

In the final lesson, we'll cover packaging and distributing your block theme.

๐ŸŽฏ Lesson Complete! You can now add dynamic functionality to your block theme with PHP.