Using the WordPress editor modal component

There are a few React components that show up over and over again when you’re developing custom block types—things like TextComponent and CheckboxControl.

One that doesn’t get much of a workout (and, indeed, I hadn’t ever used it until I started contemplating a block that would have a significant amount of text (code, actually) associated with it.

For me, the idea of a modal within the editor view of a block seemed counterintuitive. Or rather, I couldn’t figure out how the pieces would fit together—isn’t the thing that makes the modal pop up already a component?

But it just really isn’t that complicated. And it’s also a perfect example of a core concept of React, namely that things rerender when relevant states change.

So, yes, the component that triggers the modal is just some other ordinary component. The textbook example would be a button, and what you’re trying to respond to is the button being clicked. So:

<Button onClick={ openModal } >
                Clicksome
            </Button>

And when a click event occurs—this is drawn straight from the Modal Component documentation, by the way—a function is called. In this case, it’s openModal, which does nothing more than change a state variable that determines whether the modal is visible or not:

  const openModal = () => setOpen( true );
    const closeModal = () => setOpen( false );

The state in question is called isOpen and we set it up (along with it’s setter function) like so:

    const [ isOpen, setOpen ] = useState( false );

Thus the state starts out being false. Now we add a section to the JSX our component returns. And this is where the Modal component sits, nested in a dynamic bit (within curly braces, that is) that either includes the Modal component or doesn’t. It uses a trick you’re probably already familiar with to create a kind of de facto if-then statement, but without actually using if-then because you can’t do that inside of JSX.

{ isOpen && (
                <Modal title="This is my modal" onRequestClose={ closeModal }>
                    <Button variant="secondary" onClick={ closeModal }>
                        My custom close button
                    </Button>
                </Modal>
            ) }

So, if the isOpen state is true, then the second part of the statement (the part with the Modal component) is evaluated (that is, it’s included in the returned render) or, if the state is false, evaluation stops right there and what follows after the && is ignored.

Notice that the Modal component supports an onRequestClose attribute, which points to our same helper function to toggle the isOpen state to false.

The modal component can contain, as far as I know, anything else you might want to throw in there. So let’s throw in a checkbox:

 <Modal title="This is my modal" onRequestClose={ closeModal }>
                    <CheckboxControl
                        label="Required"
                        checked={attributes.required}
                        onChange={(value) => setAttributes({ required: value })}
                    />
                    <Button variant="secondary" onClick={ closeModal }>
                        My custom close button
                    </Button>
                </Modal>

So now we’ll get this modal:

Swinging back around to where we started, note that we also don’t have to use a button to trigger the modal. We can, for instance, use our onClick attribute within a TextControl tag:

<TextControl
    id={attributes.field}
    name={attributes.field}
    label={attributes.prompt}
    onClick={ openModal }
/>

Note that—if you want someone to type something into the field (though, remember, this is in the editor, so you probably don’t intend for anyone to actually use this field for data), they first have to view and close the modal. Aside from that, it just works.