Skip to content

Conditionally Display (Inner)Blocks

Been adding a few blocks to IndieBlocks where I output certain elements only if there really is something to show.

Static Blocks

First, there’s the “new” Bookmark, Like, Reply and Repost blocks. These all accept InnerBlocks, which are then wrapped in either <div class="e-content"></div> or <blockquote class="e-content"></div>.

But! If there are no “inner blocks,” I don’t want to output that empty wrapper (and potentially mess up your pages’ layout)!

If I just did …

save: ( props ) => el( 'div', useBlockProps.save(),
  el( InnerBlocks.Content )
)

… or something, that div would always be there. Don’t want that. (I’m obviously skipping a lot of code here, but this is the type of example you often see.)

The solution I settled on, for now, is to pretty much count the number of inner blocks, and then “look inside.”

If there’s two blocks, even when they’re both empty, I’m going to output that wrapper. If there’s only one and it happens to be an empty paragraph—a very common scenario—then I won’t. And so on, although I don’t cover every single use case, obviously. The result of this exercise then gets stored in a boolean empty attribute.

Then in the block’s save function—where, it seems, you can’t just go and count InnerBlocks anymore, that’s why we do it in edit—I check against empty.

What’s important is these are all static blocks. There’s no PHP “render callback.” Whatever is saved inside the editor is also shown on the front-end. And when the plugin is disabled, these blocks will still work (although you won’t be able to edit them anymore, not without converting them to another type of block).

(Semi-)Dynamic Blocks

Okay, so that’s one approach. The second one I stole from WordPress core’s “Comments” block, which short-circuits (i.e., shows nothing) when comments are closed and there aren’t any—if comments were still open, a heading and form would be shown instead.

IndieBlocks’ brand-new Interactions block is very similar. It shows a “facepile” of people who’ve liked, reposted or bookmarked a post and subsequently sent a webmention1. And I wanted it to have a customizable title, so that you could name it “Likes and Reposts,” or “Reactions From Elsewhere,” or whatever.

For a lot of posts, though, there’s not going to be any likes, reposts, and so on, in which case no title should be shown.

Oh, and the Interactions block is a dynamic block: it should reflect new or deleted reactions immediately.

So, what I ended up going for is the following: I made the Interactions block accept InnerBlocks (and not much else), and gave it default block template of an h2 heading and a new “Interactions Content” block.

And here’s the trick: while the Interactions Content block is entirely dynamic and uses PHP to fetch avatars and whatnot, the “parent” Interactions block is not.

Or rather, it is dynamic, but its “inner blocks” do get saved (per that exact little code snippet we saw before) in the editor, and its server-side callback contains little more than a query to check if there’s something to show at all. If not, it returns an empty string. If there is, however, it returns … its “static” contents, that is, its inner blocks, the “static” heading, and the not-at-all static Interactions Content (or any other blocks that were added in the meantime)!

return $block->render( array( 'dynamic' => false ) );

  1. It kind of only works with IndieBlocks’ webmentions, though. It won’t yet show the Webmention plugin’s “like” or “repost” mentions (but that plugin has its own “facepile” solution, so you should be covered either way).

Replies

  1. Jan Boddez on

    Neat Gutenberg trick: you can have a block accept (and store) InnerBlocks, yet still employ a server-side render callback in which you can run all sorts of logic before returning … the block’s “static” contents.
    Works for things other than InnerBlocks, too, by the way.
    The trick is $block->render( array( ‘dynamic’ => false ) ), which returns whatever was saved in the editor. And that can still contain dynamic content, by means of InnerBlocks.