Product design skills

references/pattern-registration.md

A supporting file of the wp-patterns skill.

Pattern Registration

Auto-Registration via /patterns/ Directory

WordPress 6.0+ automatically registers patterns from .php files in the theme's patterns/ directory.

File Header (Required Fields)

<?php
/**
 * Title: Hero with Call to Action
 * Slug: theme-slug/hero-cta
 * Categories: banner, call-to-action
 */
?>
<!-- wp:group ... -->

All Header Fields

FieldRequiredDescriptionExample
TitleYesDisplay name in inserterHero with Call to Action
SlugYesUnique identifier (namespace/name)theme-slug/hero-cta
CategoriesYesComma-separated category slugsbanner, call-to-action
DescriptionNoTooltip/help text in inserterA full-width hero section
Viewport WidthNoPreview width in inserter (px)1400
InserterNoShow in inserter (yes/no)no (hides from inserter)
KeywordsNoComma-separated search keywordshero, header, banner
Block TypesNoBlocks this pattern replacescore/post-content
Post TypesNoRestrict to post typespage, wp_template
Template TypesNoTemplate types this pattern suggests forhome, front-page

File Structure

my-theme/
  patterns/
    hero-cta.php
    testimonial-grid.php
    footer-columns.php
  theme.json
  style.css
  ...

File names are descriptive but don't affect registration — the Slug header is the identifier.

PHP in Pattern Files

Pattern files execute PHP at registration time (not render time). This is critical:

Safe Functions

<?php esc_html_e( 'Read More', 'theme-textdomain' ); ?>
<?php esc_attr_e( 'Submit', 'theme-textdomain' ); ?>
<?php echo esc_url( get_theme_file_uri( 'assets/images/placeholder.webp' ) ); ?>
<?php echo esc_attr( get_theme_file_uri( 'assets/images/bg.jpg' ) ); ?>

Always use:

  • esc_html_e() / esc_html__() for visible text (i18n + escape)
  • esc_attr_e() / esc_attr__() for attribute values (i18n + escape)
  • esc_url() for URLs
  • get_theme_file_uri() for theme asset paths

Unsafe — Do NOT Use

// WRONG: query-dependent — runs at init, not render
<?php $posts = get_posts(); ?>
<?php the_title(); ?>
<?php wp_get_current_user(); ?>
<?php is_admin(); ?>

Pattern PHP executes once when the pattern is registered (during init), not when the page renders. Any dynamic runtime data will be stale or unavailable.

i18n Requirements

All user-visible strings must be translatable:

<!-- wp:heading -->
<h2 class="wp-block-heading"><?php esc_html_e( 'Our Services', 'theme-textdomain' ); ?></h2>
<!-- /wp:heading -->

<!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'Get Started', 'theme-textdomain' ); ?></a></div>
<!-- /wp:button -->

For block attribute values containing translatable text, escape appropriately:

<!-- wp:image {"alt":"<?php esc_attr_e( 'Team photo', 'theme-textdomain' ); ?>"} -->

Manual Registration

For plugins or conditional patterns, use register_block_pattern():

register_block_pattern(
    'my-plugin/testimonial-card',
    array(
        'title'       => __( 'Testimonial Card', 'my-plugin' ),
        'description' => __( 'A single testimonial with avatar and quote.', 'my-plugin' ),
        'categories'  => array( 'testimonials' ),
        'content'     => '<!-- wp:group ... --> ... <!-- /wp:group -->',
        'keywords'    => array( 'quote', 'review' ),
    )
);

Hook into init:

add_action( 'init', function() {
    register_block_pattern( ... );
});

Custom Categories

Register before patterns:

add_action( 'init', function() {
    register_block_pattern_category(
        'theme-slug-portfolio',
        array( 'label' => __( 'Portfolio', 'theme-textdomain' ) )
    );
});

For themes, categories can also be registered via theme.json:

{
    "patterns": {
        "categories": [
            { "name": "theme-slug-portfolio", "label": "Portfolio" }
        ]
    }
}

Unregistering Patterns

Remove core or plugin patterns:

add_action( 'init', function() {
    unregister_block_pattern( 'core/query-standard-posts' );
});

Remove an entire category:

unregister_block_pattern_category( 'banner' );

Namespace Conventions

  • Themes: theme-slug/pattern-name (e.g., twentytwentyfive/hero-banner)
  • Plugins: plugin-slug/pattern-name (e.g., woocommerce/product-grid)
  • Core: core/pattern-name

Always prefix with your theme/plugin slug to avoid collisions.

On this page