Block Patterns

Patterns are prewritten block markup inserted as an editable copy, unlike synced patterns, which stay tied to one stored source.

Syntaxregister_block_pattern( string $pattern_name, array $pattern_properties ): bool

A pattern is a chunk of block markup registered under a name so users can insert it from the inserter. What lands in the post is a plain copy: from that moment it is ordinary content, and later edits to the pattern do not reach posts that already used it.

Registering in PHP

register_block_pattern( $name, $properties ) (WordPress 5.5) requires title and content, and also accepts description, categories, keywords, viewportWidth, blockTypes, postTypes, templateTypes, inserter and source. Call it on init. Categories are separate: register_block_pattern_category(), also 5.5. Core ships buttons, columns, gallery, header, text, featured and query.

The theme shorthand

Since WordPress 6.0, any PHP file in a theme's /patterns directory carrying a file header is registered automatically — the same registration, expressed as headers instead of an array:

<?php
/**
 * Title: Call to action
 * Slug: mytheme/cta
 * Categories: featured, text
 * Viewport Width: 1200
 */
?>
<!-- wp:paragraph --><p>Book a call.</p><!-- /wp:paragraph -->

Each header key maps onto the property of the same name; the file body becomes content, and because it is PHP you can call esc_html__() or get_theme_file_uri() inside it.

Not the same as…

Synced patterns (formerly reusable blocks) are stored as wp_block posts and referenced by ID, so editing one changes every place it appears. Templates (wp_template, wp_template_part) describe the page around the content and are selected by the template hierarchy, not inserted by hand.

Example

Example · php
add_action( 'init', 'agency_register_patterns' );

function agency_register_patterns(): void {
	register_block_pattern_category(
		'agency/promos',
		array( 'label' => __( 'Promos', 'agency' ) )
	);

	register_block_pattern(
		'agency/cta-banner',
		array(
			'title'         => __( 'CTA banner', 'agency' ),
			'description'   => _x( 'Full-width heading with a button beneath it.', 'Block pattern description', 'agency' ),
			'categories'    => array( 'agency/promos', 'featured' ),
			'keywords'      => array( 'cta', 'call to action', 'banner' ),
			'viewportWidth' => 1200,
			'postTypes'     => array( 'page' ), // Hidden in the post editor.
			'source'        => 'plugin',        // 'plugin' or 'theme'.
			// content is raw block markup. Escape any interpolated text here,
			// because nothing escapes it later.
			'content'       => '<!-- wp:group {"align":"full"} -->
<div class="wp-block-group alignfull"><!-- wp:heading {"level":2} -->
<h2 class="wp-block-heading">' . esc_html__( 'Ready to start?', 'agency' ) . '</h2>
<!-- /wp:heading -->
<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button">' . esc_html__( 'Book a call', 'agency' ) . '</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:group -->',
		)
	);
}

When to use it

  • Giving an editorial team approved page sections they can insert and then edit freely
  • Shipping a theme with ready-made hero, pricing and testimonial layouts
  • Removing core patterns a client should never see in the inserter

More examples

A theme pattern file with the full header

Saved as patterns/team-grid.php in the theme, this needs no registration call at all. The PHP body is what makes theme patterns worth using over a plain string: get_theme_file_uri() resolves the image path wherever the theme is installed, and the text is translatable. Block Types: core/columns makes it offered as a starting layout when a user inserts a Columns block.

Example · php
<?php
/**
 * Title: Team grid
 * Slug: mytheme/team-grid
 * Categories: columns, featured
 * Keywords: team, people, staff
 * Description: Three columns, each with an image and a name.
 * Viewport Width: 1280
 * Block Types: core/columns
 * Post Types: page
 * Inserter: yes
 */
?>
<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:image -->
<figure class="wp-block-image"><img src="<?php echo esc_url( get_theme_file_uri( 'assets/person.jpg' ) ); ?>" alt=""/></figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p><?php echo esc_html_x( 'Name', 'Team grid placeholder', 'mytheme' ); ?></p>
<!-- /wp:paragraph --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

Stripping core patterns from a client site

Reach for this when a client's inserter is cluttered with patterns that do not match their design system. Hook at a later priority than core's own registration or the unregister call finds nothing. should_load_remote_block_patterns also stops the outbound request to wordpress.org, which matters on locked-down hosting.

Example · php
add_action( 'init', 'agency_trim_patterns', 20 ); // After core registers its own.

function agency_trim_patterns(): void {
	// Remove whole categories from the inserter.
	foreach ( array( 'buttons', 'gallery' ) as $category ) {
		unregister_block_pattern_category( $category );
	}

	// Or drop individual patterns by their namespaced name.
	unregister_block_pattern( 'core/query-standard-posts' );
}

// The blunt instrument: turn off core patterns and the remote pattern
// directory entirely, leaving only what the theme and plugins register.
add_action( 'after_setup_theme', function (): void {
	remove_theme_support( 'core-block-patterns' );
} );
add_filter( 'should_load_remote_block_patterns', '__return_false' );

A pattern used as a page starter

postTypes plus templateTypes turns a pattern into a page starting point rather than something buried in the inserter. Keeping the markup in a separate .html file next to the plugin is easier to maintain than a multi-hundred-line PHP string — copy the markup straight out of the code editor view of a page you built by hand.

Example · php
add_action( 'init', 'agency_register_page_starter' );

function agency_register_page_starter(): void {
	register_block_pattern(
		'agency/service-page',
		array(
			'title'         => __( 'Service page', 'agency' ),
			'categories'    => array( 'featured' ),
			// Offered in the modal that appears when creating a new page.
			'postTypes'     => array( 'page' ),
			'templateTypes' => array( 'page' ),
			'viewportWidth' => 1400,
			'content'       => file_get_contents( __DIR__ . '/patterns/service-page.html' ),
		)
	);
}

Discussion

  • Be the first to comment on this lesson.