0

I have two custom blocks I created, Foo and Bar. I'm setting multiple to false in supports in my block.json files so that only one instance of each block can be used in a single post. But what if I want my two blocks to be mutually exclusive? Can I disable Foo if and only if Bar is already used in the current post, and vice versa, disable Bar if Foo is already used?


Edit: here's some additional details. I'm not really interested in sidesteps or workarounds a specific situation, hence the very generic example given above; I'm just trying to figure out if there's a built-in way to restrict block usage based on the present (or absence) of other blocks, much like using multiple: false does to restrict blocks to a single instance per post.

Here's a possible use case, if that helps. Let's say you have a website for your craftsmanship activity with posts showcasing various items you make, and custom blocks to provide some specific details of your creations. You may have a Wood block that describes the type of wood used in a woodturning item, or a Minerals block to list the minerals used for a jewelry work. You don't want this two blocks to be used at the same time, because jewelry is not about wood and woodturning is not about minerals, so you want a way to prevent adding one in the editor if the other is already present.

Again, I'm not looking for a workaround, there are obvious ones, like filtering the post content on each save to disable one block if the other is present. It's not ideal but it would work fine. What I'm trying to achieve instead is to prevent this from happening in the editor, just like support: false does by preventing adding more than one instance of the block directly from inside the editor.

3
  • what's the reason for this? By making your example as generic as possible you've eliminated a possible solution that sidesteps the question entirely and is easy to implement for this exact situation, but I can't write an answer as a result because it doesn't answer the question as it was written for all use cases, only some. Can you provide a concrete example?
    – Tom J Nowell
    Feb 19 at 21:52
  • Thanks @TomJNowell, I updated my question with more details. You'll see that I'm actually not looking for workarounds, I can figure that part myself; what I can't figure out is how to tackle this directly in the editor, at block level.
    – caercam
    Feb 20 at 10:39
  • I've written something up, I think you misunderstood when I said sidestep, and when I say you made it too generic I mean in the sense that the only canonical answer is "it depends" which isn't something I can post. You can ask a super generic question but it means unless there's a general answer which there rarely is you won't get anything other than a no, even if a nice elegant answer can be written for the problem that prompted you to ask it.
    – Tom J Nowell
    Feb 20 at 10:57

1 Answer 1

1

You may have a Wood block that describes the type of wood used in a woodturning item, or a Minerals block to list the minerals used for a jewelry work. You don't want this two blocks to be used at the same time, because jewelry is not about wood and woodturning is not about minerals, so you want a way to prevent adding one in the editor if the other is already present.

A possible solution is to make them the same block and instead distinguish them with block variants.

As an example, there is no Youtube Vimeo or TikTok block in WordPress, they're all block variants of core/embed. WP tells them apart based on the values of their attributes and they're listed as if they were independent blocks, complete with their own icons, descriptions, names, etc. Their edit components can even use the same logic to customize their UI.

With this specific in mind, a generic material list block with multiple: false would solve your problem. Set it so that it doesn't support insertion, then create 2 block variants that do support insertion, one for wood, and one for minerals. Now you have a minerals and a wood block both listed in the inserter, only one can be inserted, and you've halved the code you needed to write.

Notice though, that while this gives you what you wanted/needed, it doesn't give you what you asked for if we're being strict.


A Super Fiddly Alternative That Does Do What You Asked For

What you literally asked for, aka the question not the problem, can sort of be done, but it's super fiddly, and a very very bad idea. You should avoid this!

In your edit component, register both blocks again, but with the insert supports toggled. Doing this updates the block

However:

  • If you get this wrong you won't be able to insert either block without a hard refresh
  • Removing the block will be precarious, you'll need to subscribe to the core data store and watch for the blocks removal so that you can enable them both again.
  • This can all be ruined by someone copy pasting or drag dropping from somewhere else.

So there is no neat and easy solution, you can approximate the proposed solution you've devised, but it has major flaws. The fundamental problem is that you have 2 separate blocks doing the same thing but with different content, that content should be content/attributes/child blocks/etc

A word of warning though: You might be tempted to unregister a block, then re-register it. However if there are any instances of that block the entire editor will crash the moment it's unregistered. Unregistering is unnecessary.

A Side Note On Using Composition To Solve This

Sometimes the answer is composition, composing multiple blocks together, rather than having lots of single use monolithic blocks. A sign you've made this mistake is if the block has a serialised array of "items" or "things".

Core blocks such as galleries and lists used to make this mistake and it lead to lots of problems and weirdness. Making galleries containers for image blocks, and lists containers for list-item blocks solved this while opening up lots of opportunities to filtering and extension. It also eliminated a lot of code, why build special controls for moving gallery and list items around in an array when the block UI controls do it for you?

Given the example you provided, the list of minerals, or wood could be a containing block that held "material-item" blocks, then filled in at runtime the same way query blocks or recent posts blocks are filled in. This would let you separate the source of the data too making a CPT/Taxonomy an option, or an external data source, or even manually defined values.

Child blocks also have the advantage that they can appear in multiple places, and have their own attributes UI and logic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.