How to use SlotFill to extend the editor UI (JS) in WordPress

Contents

Overview — what SlotFill is and why use it

SlotFill is the UI composition pattern used throughout the WordPress block editor (Gutenberg) to let independent pieces of code insert UI into named places (slots) in the editor shell. It is the pattern behind many editor extension points such as sidebars, toolbar buttons, post status info panels, and more. SlotFill decouples where UI is defined from where UI is rendered: a Slot is a placeholder (a named location), and any number of Fill components can provide content for that placeholder.

When to use SlotFill

  • When you need to insert UI into an existing editor area (header, status area, more menu, prepublish panel, document settings) provided by the editor shell.
  • When building multiple independent extensions that need to target the same place without knowing about each other directly.
  • When you want to create your own pluggable placeholder in a UI you control, and let other code provide content for it.

Core concepts and API

Two components are central: Slot and Fill. A Slot (by name) is the consumer side — it renders whatever Fills exist for that name. A Fill (by name) is the provider — it defines content that will be rendered into its corresponding Slot.

Basic properties and behavior

  • name (string): The identifier that pairs Slot and Fill. Must match exactly.
  • scope (optional): An additional value that can be used to scope fills to a particular slot instance when you render multiple slot instances with different scopes.
  • children: You can pass JSX as Fill children. Slot also supports a render-function child that receives the fills array so you can control the render order, wrap fills, or provide props.
  • Multiple fills appear in insertion order by default the Slot can reorder or wrap them using a render function.
  • The editor provides a SlotFillProvider in the editor environment, so when building inside the block editor you usually do not need to add your own provider.

Where SlotFill is already used in the editor

  • PluginSidebar (the right-hand plugin sidebar)
  • PluginPrePublishPanel (pre-publish checks)
  • PluginMoreMenuItem (the More menu)
  • PluginPostStatusInfo (the post status area near the publish button)
  • Various header and toolbar extension points

How to use SlotFill — practical examples

Below you will find multiple real examples you can use in a plugin. Each example is given both as the editor-facing JavaScript and, where needed, the PHP needed to enqueue it within WordPress.

1) Minimal ESNext plugin adding a small widget to the post status area

This example assumes you use the standard @wordpress/scripts build toolchain (so you can import packages). It inserts a small status indicator into the post status area using the Fill name that the edit-post package exposes (PluginPostStatusInfo).

import { Fill } from @wordpress/components
import { registerPlugin } from @wordpress/plugins
import { useSelect } from @wordpress/data
import { __ } from @wordpress/i18n
import { Fragment } from @wordpress/element

function PostStatusLengthIndicator() {
  const title = useSelect( (select) => select(core/editor).getEditedPostAttribute(title)   )
  const length = title.length

  return (
    ltFill name=PluginPostStatusInfogt
      ltdiv style={{ paddingLeft: 8, display: flex, alignItems: center }}gt
        ltspan aria-hidden=true style={{ opacity: 0.7 }}gt✎lt/spangt
        ltdiv style={{ marginLeft: 6 }}gt
          ltstronggt{__(Title length:, my-text-domain)}lt/stronggt {length}
        lt/divgt
      lt/divgt
    lt/Fillgt
  )
}

registerPlugin(my-post-status-length, {
  render: PostStatusLengthIndicator,
})

Enqueue this script from PHP so it runs only inside the block editor:

lt?php
/
  Plugin bootstrap file
 /

function my_slotfill_enqueue_editor_assets() {
    wp_enqueue_script(
        my-slotfill-editor,
        plugins_url( build/index.js, __FILE__ ),
        array( wp-plugins, wp-edit-post, wp-element, wp-components, wp-data, wp-i18n ),
        filemtime( plugin_dir_path( __FILE__ ) . build/index.js ),
        true
    )
}
add_action( enqueue_block_editor_assets, my_slotfill_enqueue_editor_assets )

2) Using globals (no build tool) — the equivalent approach

If you do not use a bundler, you can write a script that relies on the global wp. objects that Gutenberg exposes. This example performs the same insertion into PluginPostStatusInfo.

( function( wp ) {
  const { Fill } = wp.components
  const { registerPlugin } = wp.plugins
  const { select } = wp.data

  function PostStatusLengthIndicator() {
    const title = select(core/editor).getEditedPostAttribute(title)  
    return wp.element.createElement(
      Fill,
      { name: PluginPostStatusInfo },
      wp.element.createElement(div, { style: { paddingLeft: 8, display: flex, alignItems: center } },
        wp.element.createElement(span, { aria-hidden: true, style: { opacity: 0.7 } }, ✎),
        wp.element.createElement(div, { style: { marginLeft: 6 } },
          wp.element.createElement(strong, null, Title length:),
           ,
          title.length
        )
      )
    )
  }

  registerPlugin(my-post-status-length-global, { render: PostStatusLengthIndicator })
})( window.wp )

3) Creating a custom Slot in your own UI and filling it

SlotFill is useful even inside your own component tree. Render a Slot where you want content to be inserted render Fill elsewhere (the Slot and Fill must render under the same SlotFillProvider — the editor provides one when inside the block editor if outside you can provide your own).

import { Slot, Fill } from @wordpress/components
import { registerPlugin } from @wordpress/plugins

function MyPluginHost() {
  // This Slot will render fills that target MyPlugin/CustomToolbar
  return (
    ltdivgt
      lth3gtMy Plugin UIlt/h3gt
      ltdiv className=my-plugin-toolbargt
        ltSlot name=MyPlugin/CustomToolbargt
          { ( fills ) =gt (
            ltdiv style={{ display: flex, gap: 8 }}gt
              { fills.map( ( FillComponent, i ) =gt ltFillComponent key={ i } /gt ) }
            lt/divgt
          ) }
        lt/Slotgt
      lt/divgt
    lt/divgt
  )
}

function RemoteFill() {
  return (
    ltFill name=MyPlugin/CustomToolbargt
      ltbuttongtRemote Actionlt/buttongt
    lt/Fillgt
  )
}

registerPlugin(my-plugin-host, { render: MyPluginHost })
registerPlugin(my-plugin-remote-fill, { render: RemoteFill })

4) Advanced: using Slot render-prop to pass props into fills

Slot supports a child function that receives the fills array. This lets you render fills yourself and pass props into them. That pattern allows the host to control how fills are integrated, to set ordering rules, or to provide contextual props.

import { Slot, Fill } from @wordpress/components
import { registerPlugin } from @wordpress/plugins

function HostWithProps() {
  const hostContext = { color: purple }

  return (
    ltSlot name=MyPlugin/PropSlotgt
      { ( fills ) =gt (
        ltdivgt
          { fills.map( ( FillComponent, idx ) =gt
            ltFillComponent key={ idx } hostContext={ hostContext } /gt
          ) }
        lt/divgt
      ) }
    lt/Slotgt
  )
}

function FillConsumer( { hostContext } ) {
  return ltdiv style={{ color: hostContext.color }}gtI use host propslt/divgt
}

function ProvideFill() {
  return ltFill name=MyPlugin/PropSlotgt{( props ) =gt ltFillConsumer { ...props } /gt}lt/Fillgt
}

registerPlugin( my-prop-slot-host, { render: HostWithProps } )
registerPlugin( my-prop-slot-fill, { render: ProvideFill } )

5) Styling fills

You can enqueue CSS the usual way (enqueue_block_editor_assets) or add a small inline stylesheet. Keep styles scoped to your plugin classes to avoid collisions.

.my-plugin-toolbar { 
  display: flex
  gap: 8px
  align-items: center
}
.my-plugin-toolbar button {
  background: #fff
  border: 1px solid #ddd
  padding: 6px 10px
  border-radius: 4px
}

Practical pointers and best practices

  • Use edit-post helper components when available: The edit-post package provides PluginSidebar, PluginPrePublishPanel and other convenience components that wrap slot/fill logic and provide consistent UX with WordPress. Prefer those components when they match your need.
  • Enqueue your script with the editor-only hook: Use enqueue_block_editor_assets so your slot/fill code loads only in the block editor.
  • Use wp.data selectors and hooks: Keep UI stateless where possible and rely on useSelect and useDispatch to read and update editor state.
  • Scope if you need multiple slot instances: When you render multiple slot instances (for example multiple editors on the same page), use the scope prop to separate the fills for each instance so fills only appear in the intended slot.
  • Accessibility and keyboard focus: Ensure injected controls are accessible, keyboard reachable, and provide ARIA labels if needed.
  • Localization: Use __() and related functions from @wordpress/i18n for strings displayed inside fills.
  • Performance: Avoid heavy computations in the render path of fills memoize or use selectors wisely to avoid unnecessary re-renders.

Troubleshooting — common issues

  1. Nothing appears: Confirm the name matches exactly between Slot and Fill, and that your script is actually loading in the editor (use browser devtools). Confirm you enqueue with enqueue_block_editor_assets.
  2. Slot provider not present (you see nothing for a Slot you added in a context outside the editor): If rendering Slot/Fill outside the editor shell you must provide a SlotFillProvider from @wordpress/components around the parts of the tree using Slots and Fills.
  3. Ordering or grouping isnt what you expect: Use the Slot render-prop to inspect the fills array and control ordering directly, or have fills include their own ordering metadata so the Slot can sort them before rendering.
  4. Conflicting CSS: Scope styles to plugin-specific classes to avoid accidental overrides.

Compatibility notes and package references

If you target older WordPress versions, verify that the editor packages you rely on are present. For example the edit-post conveniences (PluginSidebar, PluginPrePublishPanel, etc.) require specific Gutenberg versions. When building with a toolchain you will import from packages such as:

  • @wordpress/components — provides Slot and Fill, plus UI building blocks.
  • @wordpress/plugins — registerPlugin used to mount your plugin component into the editor lifecycle.
  • @wordpress/edit-post — convenience components that themselves use SlotFill.
  • @wordpress/data — useSelect and useDispatch to interact with editor state.

Examples summary table

Scenario API to use Notes
Insert control in post status area Fill name=PluginPostStatusInfo / registerPlugin Common small contextual info near the Publish/Update button
Create sidebar panel PluginSidebar (edit-post) Preferred over raw SlotFill for sidebars — consistent styling and behavior
Custom internal extensibility Slot Fill Great for letting other parts of your plugin contribute UI to a shared area

Further reading

Official documentation and API reference are valuable for up-to-date slot names and helper components. Start at the WordPress block editor package docs and the components package docs:

Final note

SlotFill is a flexible, composable, and well-established pattern inside the block editor. Use the provided edit-post helper components when possible for consistent UX otherwise, use Slot and Fill to create or consume custom extension points, combine with wp.data for reactive data-driven UI, and keep styling and accessibility in mind.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *