Access block data with PHP using parse_blocks() and render_block()

The Gutenberg block editor organizes your content into discrete blocks, and WordPress includes functions for easily accessing the individual blocks within a post.

Here’s a quick summary of how the functions work, and examples of them in use.

Jump to Section:

  1. parse_blocks()
  2. render_block()
  3. Display blockquote from post
  4. Table of contents from headings
  5. ACF block data

parse_blocks()

This function converts the HTML comments and markup stored in post_content into an array of parsed block objects.

Usage: $blocks = parse_blocks( $post->post_content );

The post_content of my Style Guide looks like:

<!-- wp:paragraph {"fontSize":"large"} -->
<p class="has-large-font-size">Lorem <strong>ipsum</strong> dolor <em>sit amet</em>, consectetur <a class="map-link" href="https://maps.google.com/?q=Houston,+TX">adipiscing elit</a>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":1} -->
<h1>Heading 1</h1>
<!-- /wp:heading -->

After running it through parse_blocks(), it looks like:

Array
(
    [0] => Array
        (
            [blockName] => core/paragraph
            [attrs] => Array
                (
                    [fontSize] => large
                )

            [innerBlocks] => Array
                (
                )

            [innerHTML] => 
<p class="has-large-font-size">Lorem <strong>ipsum</strong> dolor <em>sit amet</em>, consectetur <a class="map-link" href="https://maps.google.com/?q=Houston,+TX">adipiscing elit</a>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

            [innerContent] => Array
                (
                    [0] => 
<p class="has-large-font-size">Lorem <strong>ipsum</strong> dolor <em>sit amet</em>, consectetur <a class="map-link" href="https://maps.google.com/?q=Houston,+TX">adipiscing elit</a>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

                )

        )

    [1] => Array
        (
            [blockName] => 
            [attrs] => Array
                (
                )

            [innerBlocks] => Array
                (
                )

            [innerHTML] => 


            [innerContent] => Array
                (
                    [0] => 


                )

        )

    [2] => Array
        (
            [blockName] => core/heading
            [attrs] => Array
                (
                    [level] => 1
                )

            [innerBlocks] => Array
                (
                )

            [innerHTML] => 
<h1>Heading 1</h1>

            [innerContent] => Array
                (
                    [0] => 
<h1>Heading 1</h1>

                )

render_block()

This function takes a single parsed block object and returns the rendered HTML for that block.

Usage: echo render_block( $block );

Note: While this function does convert the block object into the rendered HTML, it does not run all the additional filters that are typically applied to the content, like converting lines to paragraphs, rendering shortcodes, and rendering embeds.

In many cases, you’ll want to use the the_content filter to add these extras. Example: echo apply_filters( 'the_content', render_block( $block ) );

Display blockquote from post

On my portfolio archive page, every 4th project includes a blockquote. You could pull this directly from the content of the portfolio item by looping through the blocks and displaying the core/quote block, if one is found.

Add this function to your theme, then call be_display_post_blockquote() to display the quote.

/**
 * Display blockquote from post 
 * @link https://www.billerickson.net/access-gutenberg-block-data/
 */
function be_display_post_blockquote() {
  global $post;
  $blocks = parse_blocks( $post->post_content );
  foreach( $blocks as $block ) {
    if( 'core/quote' === $block['blockName'] ) {
      echo render_block( $block );
      break;
    }
  }
}

Table of contents from headings

In a previous tutorial I showed how to dynamically build a table of contents by parsing the HTML.

Converting the HTML to blocks makes this much easier. You loop through the blocks and find the core/heading ones.

The example below makes a simple ordered list of all the headings in the page. You could take this a step further and use $block['attrs']['level'] to make a nested list based on the heading level (ex: h3 is subordinate to h2).

/**
 * List post headings 
 * @link https://www.billerickson.net/access-gutenberg-block-data/
 */
function be_list_post_headings() {
	global $post;
	$blocks = parse_blocks( $post->post_content );
	$headings = array();
	foreach( $blocks as $block ) {
		if( 'core/heading' === $block['blockName'] )
			$headings[] = wp_strip_all_tags( $block['innerHTML'] );
	}
	if( !empty( $headings ) ) {
		echo '<ol class="table-of-contents">';
		foreach( $headings as $heading )
			echo '<li>' . $heading . '</li>';
		echo '</ol>';
	}
}

Unfortunately WordPress does not store the heading’s ID as an anchor attribute, so you’ll still need to manipulate the HTML if you want to create anchor links (example).

ACF block data

If you have built a custom block with Advanced Custom Fields, you can easily access all the block data using this method.

ACF generates dynamic blocks. Rather than storing actual HTML, it stores all of the block data in the HTML comments and then dynamically renders it using the PHP file or function you specify.

I’m working on a site that includes a “Service” block. It has fields for title, anchor link, content, and “success story” (a linked post from elsewhere).

The homepage includes links to the top-level pages, as well as anchor links to each individual service block. I’m using parse_blocks() to find all of the acf/service blocks, then building the links using the $block['attrs'] data.

/**
 * Get service sections
 * @link https://www.billerickson.net/access-gutenberg-block-data/
 */
function ea_get_service_sections() {

	$sections = array();

	global $post;
	$blocks = parse_blocks( $post->post_content );
	foreach( $blocks as $block ) {
		if( 'acf/service' !== $block['blockName'] )
			continue;

		$title = $anchor = '';
		if( !empty( $block['attrs']['data']['title'] ) )
			$title = $block['attrs']['data']['title'];

		if( !empty( $block['attrs']['data']['anchor'] ) )
			$anchor = $block['attrs']['data']['anchor'];

		if( empty( $anchor ) )
			$anchor = $title;

		$sections[] = '<a href="' . get_permalink() . '#' . sanitize_title_with_dashes( $anchor ) . '">' . esc_html( $title ) . '</a>';
	}

	if( empty( $sections ) )
		return;

	echo '<ul class="service-sections">';
	foreach( $sections as $section )
		echo '<li>' . $section . '</li>';
	echo '</ul>';
}

Join Our Team at CultivateWP

We design and build high-performance custom WordPress themes for market leading web publishers. We're looking for designers & developers to join our team.

Learn More

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. Camilo T says

    Hello, I am learning this from wordpress and above all about gutenberg, but I cannot get the nodes or items from the core / gallery
    Do you have a way to do it or a tutorial like this to learn a little more about the gallery?

    • Bill Erickson says

      It looks like the core/gallery block includes an array of the attachment ids used in the gallery. You can access it in $block['attrs']['ids'].

      Here’s some testing code I added to a local site:

      add_action( 'wp_footer', function() {
      	global $post;
      	$blocks = parse_blocks( $post->post_content );
      	foreach( $blocks as $block ) {
      		if( 'core/gallery' === $block['blockName'] ) {
      			ea_pp( $block );
      		}
      	}
      });
      

      And here’s the result: https://a.cl.ly/E0uz462K

      (Here’s my ea_pp() helper function).

  2. Christina says

    I am using a CPT with a template. I also want to control how the layout looks on the frontend of the site. The problem is that if you render the block based on block name, what happens when you have multiple blocks with the same name? I used the [‘atts’] and checked the array for a class name. Is there a better way that I’ve missed? Thanks!

    • Bill Erickson says

      I’m not exactly sure what you’re asking for here. Are you saying you have multiple blocks of the same type within a post and you want to pull a specific one? In that case yes, I think a custom class name would be the best way to identify it.

  3. Brian says

    Bill, this was exactly what I was looking for! This is the second time I’ve found some key information on your site. Many thanks!

  4. Corin says

    Hi Bill,

    I’m having an issue trying to access an ACF field with id “my_category” assigned to custom-block.php from insided custom-block.php without using get_field(“my_category”).

    If my_category = A, then custom-block.php would return value A. If my_category = B, then custom-block.php would return value B.

    If there are two custom-block.php inserted into a page, when I use parse_blocks and loop through $blocks in order to get

    $value = $block[‘attrs’][‘data’][“my_category”]

    I get the value of both blocks.

    Is there a way for custom-block.php to know which index in the page array it’s in?

    • Bill Erickson says

      I’m not sure I’m following. Are you using parse_blocks() inside your template files (ex: functions.php, single.php) or inside your custom block (custom-block.php)?

      If you are inside your custom block, try looking at the $block variable instead which should contain all the data about that specific block.

  5. Adeline Diniel says

    Hello!
    Thank you for your tutorial.
    However, I don’t know how to apply it to my case and maybe you could help me.

    I have a “News block” to retrieve news.
    I also added a “image_miniature” field associated to all my news, it’s an image link.

    When I display my “News block”, I would like to retrieve for each article displayed, the URL of this “image_miniature” image.
    How to write this correctly ?

    When I ask to display “parse_blocks()”, the field “image_miniature” does not appear in the news list.

    Thanks a lot !

  6. Jules says

    Hi, I’ve come back to this article quite a lot over the past year. Always very helpful. I wanted to point out one thing though.

    If you are thinking about rendering your blocks with `render_block()`, this might not always yield the desired result, especially when using embed blocks that depend on other functions.

    Often, what you actually want is `apply_filters( ‘the_content’, render_block( $block ) );`

    For more context see: https://github.com/WordPress/gutenberg/issues/14080#issuecomment-567429414

  7. Lukas says

    Hi!

    Thanks for the helpful guide.

    I’ve created an ACF block in which I’m trying to set IDs on some headings to match the heading text – e.g. “My heading” would get an ID of “my-heading”.

    I would then like to have a Table of Contents block grab those IDs and use them as anchors, so users can quickly navigate to the right section on the page.

    Problem is, in order to use the function you provided to get ACF block data from the $block array, I first have to have my data in the ACF fields for it to pick them up. However, I set those IDs dynamically using a function in my block template, that converts “My heading” to “my-heading”. As a result, my IDs never end up as values in the block fields, meaning they never end up in the $block array for me to use in the ToC-block.

    Do you have any idea of how I could go about this? Is there maybe a way I could populate the actual ID field for each block dynamically?

    Any help would be much appreciated!

    Best wishes,
    Lukas

    • Bill Erickson says

      Here’s my Table of Contents block that does exactly what you describe – adds an ID to all headings, then outputs the list of links pointing to those headings.

      This was pulled directly from my Cultivate Pro plugin so you’ll need to make some minor changes (ex: remove references to Cultivate Pro functions you don’t have) but the basic functionality is there.

Leave A Reply