When building blocks or working with the Gutenberg APIs you might have come across SlotFills.
They’re really useful and allow you to insert components in different locations from seemingly disconnected parts of your application.
A quick explanation
The way it works is, a <Slot />
component will be added somewhere in an application – and then in other parts of the application you can use a <Fill />
to “send” content to the slot.
Under the hood they use React Portals so often appear somewhere else completely in the DOM.
Gutenberg uses these under the hood for things like sidebars and block controls, if you’ve built blocks before then you’ve likely used BlockControls and the InspectorControls for building out your block admin UI.
The great thing is we can also use our own SlotsFills for things like custom admin screens, making them extensible – Nick Diego has a great article explaining how he’s used them on his custom admin screens and then extended those screens via another plugin.
Example
Here’s a quick example showing how to use Slots and Fills – checkout the Gutenberg docs for a more detailed example.
import { createSlotFill } from '@wordpress/components';
const { Fill, Slot } = createSlotFill( 'MyList' );
// The main component.
const MyComponent = () => {
return (
<>
<ListComponent />
<ListExtensionComponent />
</>
);
};
// A list component, that we want to be extensible.
const ListComponent = () => {
return (
<ul>
<li>A list item</li>
<Slot />
</ul>
);
};
// A seperate component, which uses the Fill to extend the list.
const ListExtensionComponent = () => {
return (
<Fill>
<li>Another list item</li>
</Fill>
);
};
The rendered HTML will look something like:
<ul>
<li>A list item</li>
<li>Another list item</li>
</ul>
Passing props (fillProps)
The reason I wanted to write this article was because I came upon an issue that took me a little while to figure out.
I have a Slot
in a dropdown menu so that it can be extended from different parts of my application – so far so good. But for a specific button (one that opens a modal) I wanted to close the dropdown menu on interaction.
This meant that the button needs access to the onClose()
function from the parent dropdown component – so I needed to find a way to pass props to the Fill. Que fillProps
.
After a too much trial and error I started combing the Gutenberg source code and found that there was an easy way to do this all along, it’s just not documented very well.
If we pass fillProps
to our Slot
, then we can access that in the Fill
.
Lets say we have a closePopup()
function:
function closePopup() {
console.log( 'Close the popup!' );
}
Here’s what the Slot
would look like when we use fillProps
:
<Slot fillProps={ { closePopup } } />
And then the Fill
can consume those props if we pass a function as the Fill
content:
<Fill>
{ ( { closePopup } ) => {
<Button onClick={ closePopup }>Close</Button>
} }
</Fill>
It’s similar to how we can pass functions as children
in React.
Here I’m using the SlotFill to add the Export All button to the dropdown, and then I’m using the fillProps
to pass the closePopup
function so that the popup menu is closed when clicked, which is then followed by opening the modal.
In the end all it took was some digging in the Gutenberg source code to find a prop, and the rest is already done!
I’m sure there are plenty of use cases for this, I’d be interested to hear yours.