Building a header block in WordPress

I’m building a website with two different page title styles:

Standard page title
Fancy page title

I built this as a custom Header block so we can customize the page header directly in the Gutenberg block editor. The steps to implement this are:

  1. Create the custom block with Advanced Custom Fields.
  2. Remove the standard page title if this block is used.
  3. Move the block up to touch the site header when it’s the first block on the page.

Create the header block

I use Advanced Custom Fields to build custom blocks. Here’s a detailed guide on building custom blocks with ACF.

I registered my header block and set the render template to the /partials/block-header.php file in my theme.

acf_register_block_type( array(
	'name'				=> 'header',
	'title'				=> __( 'Header', 'core-functionality' ),
	'render_template'		=> 'partials/block-header.php',
	'category'			=> 'formatting',
	'icon'				=> 'schedule',
	'mode'				=> 'auto',
	'keywords'			=> array(),
));

I went to Custom Fields > Add New and created this field group:

Then in partials/block-header.php I added the markup for the header.

Take note of the first line as it took me a bit of time to figure it out. If you need access to any information about the current post (like the title), standard template tags like get_the_title() and get_the_ID() won’t work in the Gutenberg block editor, but will work on the frontend.

ACF dynamically loads the block using AJAX and passes the post ID as $_POST['post_id']. So we’ll use that in the backend, and get_the_ID() on the frontend to get the Post ID. We use this to build the default page title ( get_the_title( $post_id ) ).

// Post ID is in $_POST['post_id'] when rendering ACF block in Gutenberg
$post_id = get_the_ID() ? get_the_ID() : $_POST['post_id'];

$title = get_field( 'title' );
if( empty( $title  ) )
	$title = get_the_title( $post_id );

$content  = get_field( 'content' );

$image = get_field( 'image' );
if( empty( $image ) )
	$image = get_option( 'options_ea_default_header' );

echo '<div class="block-header alignfull">';
	echo '<div class="block-header__image">' . wp_get_attachment_image( $image, 'ea_header' ) . '</div>';
	echo '<div class="block-header__content">';
		echo '<h1>' . esc_html( $title ) . '</h1>';
		echo wpautop( $content );
	echo '</div>';
echo '</div>';

The new header block works great, but now there are two page titles. The next step is to remove the standard page title if this block is used.

Remove the standard page title

Before Gutenberg, I would have used Genesis Title Toggle to disable the standard page title. The next version of Genesis will include the title toggle functionality, but this still isn’t as user friendly as it could be.

A better approach is to automatically remove the standard title if this block is used. We should also remove the title if an h1 heading block is used. If a block has innerBlocks (ex: columns, group…), we’ll scan through those as well.

This function will let you know if the current post has an h1 block. Add it to your theme’s functions.php file, and change acf/header to the name of your heading block.

/**
 * Recursively searches content for h1 blocks.
 *
 * @see https://www.billerickson.net/building-a-header-block-in-wordpress/
 *
 * @param array $blocks
 * @return bool
 */
function be_has_h1_block( $blocks = array() ) {
	foreach ( $blocks as $block ) {

		if( ! isset( $block['blockName'] ) )
			continue;

		// Custom header block
		if( 'acf/header' === $block['blockName'] ) {
			return true;

		// Heading block
		} elseif( 'core/heading' === $block['blockName'] && isset( $block['attrs']['level'] ) && 1 === $block['attrs']['level'] ) {
			return true;

		// Scan inner blocks for headings
		} elseif( isset( $block['innerBlocks'] ) && !empty( $block['innerBlocks'] ) ) {
			$inner_h1 = be_has_h1_block( $block['innerBlocks'] );
			if( $inner_h1 )
				return true;
		}
	}

	return false;
}

If you’re building a Genesis theme, you can use this function to unhook the heading and .entry-header markup:

/**
 * Remove entry-title if h1 block used
 * @see https://www.billerickson.net/building-a-header-block-in-wordpress/
 */
function be_remove_entry_title() {
	if( ! ( is_singular() && function_exists( 'parse_blocks' ) ) )
		return;

	global $post;
	$blocks = parse_blocks( $post->post_content );
	$has_h1 = be_has_h1_block( $blocks );

	if( $has_h1 ) {
		remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_open', 5 );
		remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_close', 15 );
		remove_action( 'genesis_entry_header', 'genesis_do_post_title' );
	}
}
add_action( 'genesis_before_entry', 'be_remove_entry_title' );

If you’re building a custom theme, you can integrate the be_has_h1_block() into the function that displays the post title. For example, my EA Starter theme can be customized like so (here’s the original):

/**
 * Entry Title
 *
 */
function ea_entry_title() {

	global $post;
	$blocks = parse_blocks( $post->post_content );
	if( ! be_has_h1_block( $blocks ) )
		echo '<h1 class="entry-title">' . get_the_title() . '</h1>';
}
add_action( 'tha_entry_top', 'ea_entry_title' )

Move block up to touch site header

The .site-inner on this site has 16px of padding on mobile and 40px on tablet/desktop.

If the header block is the first block on the page, this CSS adds negative margin to the top of the block so it is flush with the header:

.entry-content  > .block-header:first-child {
	margin-top: -16px;
	@media only screen and (min-width: 768px) {
		margin-top: -40px;
	}
}

Advanced Custom Fields Genesis Theme Framework Gutenberg Block Editor WordPress Development

Bill Erickson

Bill Erickson is a freelance WordPress developer and a contributing developer to the Genesis framework. For the past 14 years he has worked with attorneys, publishers, corporations, and non-profits, building custom websites tailored to their needs and goals.

Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments

  1. Matt Whiteley says

    This is a great approach! I’ve created a variety of hero blocks and I’ve been using page templates to ensure the title is removed from any pages using the hero block which contains the H1. This removes that step from the process! Great stuff as always Bill.

  2. Angie Vale says

    Great post, what approach would you use for having a header block for a custom post archive page?

  3. I Hate Blocks says

    In a theme without the Block Editor this is so much simpler, why waste time on doing it this way?

    • Bill Erickson says

      How is it easier in the Classic Editor? Classic Editor doesn’t have a method of building custom header areas like this.

      In my opinion, this is an example of what makes the block editor shine. What would have required a custom metabox and disjointed content creation process in TinyMCE can now be implemented in an intuitive and easy-to-use way.

      You could also pair this with a default block template so new posts/pages automatically have the header block, which can be customized or removed.

Leave A Reply