WordPress developers have relied on Hooks API since version 2.0 to extend the functionality of WordPress and until WordPress 5.0, it was the only way.
In WordPress 5.0, the WordPress SlotFill system was introduced as part of the Gutenberg project and is the extension paradigm that allows developers to extend the UIs introduced with the Gutenberg project such as the Post Editor and the Site Editor.
At a high level, the SlotFill system is an extension paradigm that allows developers to register plugins containing content, or Fills, to be displayed in a specific location, or Slot, in the UI.
How does it compare to Hooks?
Historically, if a developer wanted to add UI elements to the WordPress admin, there would be an Action exposed that they could “hook” into.
For example, if the edit_form_after_title
action was leveraged, it was possible to add a secondary title to the existing post-edit screen:
add_action(
edit_form_after_title',
function() {
echo ''<input type="text" class="widefat" placeholder="Add secondary title" />';
}
);
Which would display as:
This was possible due to the action being exposed by calling do_action()
in a specific location in the files that are loaded to render the edit post screen.
do_action( 'edit_form_after_title', $post );
Based on this, an Action can be viewed as having three steps:
- A location is exposed for extension via a
do_action()
call in WordPress core. - A developer creates a plugin ( or theme ) that contains a corresponding
add_action()
call. - WordPress loads the page and when the
do_action
is run, the custom code from the plugin is run in the location wheredo_action
was called.
SlotFill is similar to actions in the sense that they are also location-based. The actual implementation is much different, but the steps are very similar in concept
- A Slot is exposed somewhere in WordPress core.
- A developer registers a plugin with
wp.plugins.registerPlugin(
) which contains a Fill for a corresponding Slot. - WordPress loads the page where the Slot is exposed and The SlotFill system renders the Fill content in the Slot location.
The pieces of the puzzle
The SlotFill system consists of five pieces that work together to render Fill content in Slot locations:
- Slot component
- Fill component
- SlotFillProvider component
- registerPlugin()
- PluginArea component.
Slot
The Slot component is used to determine where in the UI the extension point will be exposed. Wherever this component is rendered is where the associated Fill components content will be displayed.
Slot accepts the following props:
- name: The name of the Slot.
- fillProps: Object that is passed to the Fill.
- bubblesVirtually: Changes event bubbling behavior
import { Slot } from '@wordpress/components';
<Slot
name="my-slot-name"
fillProps={ { key: 'value' } }
bubblesVirtually
/>
Fill
The Fill component is used to provide content to a Slot with the same name property.
This component can be rendered anywhere inside the UI – even in a completely different element tree. The contents of the Fill will be rendered in the Slot with the same name property. Regardless of where the Fill is rendered.
Fill accepts a single prop:
- name: The name of the Slot that this Fill is targeting.
import { Fill } from '@wordpress/components';
<Fill name="my-slot-name">
Fill Contents
</Fill>
SlotFillProvider
The SlotFillProvider component is the magical glue that connects Fills to their Slots.
It wraps the UI and its job is to detect any Slots or Fills anywhere inside of it or any of its child components and then render the Fill content in the associated Slot location. This component does not accept any props.
registerPlugin()
The only item in the list that is not a React component, this function is used to register a plugin that contains one for more fills. It is part of the @wordpress/plugins
package and it accepts two parameters:
- name A string identifying the plugin
- settings: Object containing settings for the plugin
- render: The component to render
- icon: A visual asset to be associated with the plugin
- scope: The scope this plugin belongs to. This is only used for custom implementations and should be left undefined
For more information on registerPlugin
and the other functions provided by this package, please refer to the official documentation on the plugins module.
import { registerPlugin } from '@wordpress/plugins';
registerPlugin( 'example-plugin', {
render: ComponentToRender,
icon: 'smiley',
} );
PluginArea
Finally, there is the PluginArea component. Its job is to retrieve all of the registered plugins from the plugins API and render them internally inside a hidden div element.
The PluginArea component accepts the following props:
- scope: Scope for the plugin area. Registered plugins must match the scope to be rendered. This is currently not defined in existing implementations
- onError: Function to handle errors
import { PluginArea } from '@wordpress/plugins';
<PluginArea
scope="custom-scope"
onError={ onErrorHandler }
/>;
Putting it together
Both the Post Editor and Site Editor screens implement a SlotFill system. At a high level, it looks like this:
- A
SlotFillProvider
wraps theEditorProvider
component. - Various
Slot
components are exposed in the Layout component. Fill
components are added using theregisterPlugin()
function.- The
Fill
components are rendered in hidden div by thePluginArea
component. Fill
content is rendered in the associatedSlot
location.
// Pseudo code for edit-post
<SlotFillProvider>
<EditorProvider>
<Layout>
{ /* Slots are exposed in various locations */ }
<PluginArea />
</Layout>
</EditorProvider>
</SlotFillProvider>;
A visual representation
In the visual representations below, it can be seen how the system works together. Plugins are registered using registerPlugin()
and added to the list of plugins that have been registered. The PluginArea component retrieves the the plugins and renders each plugins render
property that contains Fill components into a hidden div.
Once the PluginArea
renders the plugins, the SlotFillProvider
then detects that there are FIlls available and renders their content in the association Slot
location.
How SlotFills are built
So far in this post, examples have focused on the basic Slot component.
However, the available SlotFills are not just simple components. They are named components that usually contain other functionality and inner components.
In this example, you can view how the PluginPostStatusInfo SlotFill is structured.
const PluginPostStatusInfo = ( { children, className } ) => (
<Fill>
<PanelRow className={ className }>{ children }</PanelRow>
</Fill>
);
PluginPostStatusInfo.Slot = Slot;
export default PluginPostStatusInfo;
Notice that the exported SlotFill component contains both the Slot and the Fill. Hence the name of the component!
Exposing a Slot
Each SlotFIll is exposed in the UI using its dot-Slot property.
In this simplified example, we see how the PluginPostStatusInfo
Slot is exposed inside the PostStatus
component. The full source is available in the WordPress Gutenberg repository on GitHub.
function PostStatus( { isOpened, onTogglePanel } ) {
return (
<PanelBody
className="edit-post-post-status"
title={ __( 'Summary' ) }
opened={ isOpened }
onToggle={ onTogglePanel }
>
<PluginPostStatusInfo.Slot/>
</PanelBody>
);
}
Registering a Fill
Now that the Slot has been exposed, we can use registerPlugin
to add a Fill.
In the example below, the PluginPostStatusInfo SlotFill is imported from the wordpress/edit-post package and is used in our render property.
import { registerPlugin } from '@wordpress/plugins';
import { PluginPostStatusInfo } from '@wordpress/edit-post';
registerPlugin('example-plugin', {
render: () => (
<PluginPostStatusInfo>
<p>Post Status info SlotFill</p>
</PluginPostStatusInfo>
),
icon: 'smiley'
})
Any children of the PluginPostStatusInfo component will be rendered in the associated Slot location.
Currently available SlotFills
As of WordPress 6.1, there are a total of 12 available slots, nine in the Edit Post screen and three in the Site Editor screen. These are well-documented in the block editor handbook and include examples.
Edit Post Screen
- PluginPostStatusInfo
- PluginPrePublishPanel
- PluginPostPublishPanel
- PluginMoreMenuItem
- PluginBlockSettingsMenuItem
- PluginSidebar
- PluginSidebarMoreMenuItem
- PluginDocumentSettingPanel
- MainDashboardButton
These Slots are part of the @wordpress/edit-post
package.
import {
PluginPostStatusInfo
PluginPrePublishPanel
PluginPostPublishPanel
PluginMoreMenuItem
PluginBlockSettingsMenuItem
PluginSidebar
PluginSidebarMoreMenuItem
PluginDocumentSettingPanel
__experimentalMainDashboardButton as MainDashboardButton,
} from '@wordpress/edit-post';
Site Editor screen
These Slots are part of the @wordpress/edit-site
package. These slots link to the same documentation as their counterparts in the Edit Post screen. The difference is where the slot is exposed.
import {
PluginMoreMenuItem,
PluginSidebar,
__experimentalMainDashboardButton as MainDashboardButton,
} from '@wordpress/edit-site';
Resources to learn more
- SlotFills Reference in the block editor handbook (Documentation)
- Talk – creating a pre-publish checklist for Gutenberg (WordPress.tv 2022)
- Lightning Talk – Extending Gutenberg with SlotFill (WordPress.tv 2022)
- SlotFill examples repository (external)
Thanks to @bph, @bcworkz and @webcommsat for reviewing this article.
Leave a Reply