<Slot name="…"> call sites in the dashboard source (linked under Reference below); each entry here records the host page, the slot name, and the context your component receives.
If you need a new injection point in a built-in page, open a PR adding <Slot name="..." context={...} /> and a documentation entry here — that’s the contract for new slots.
At a glance
Ambient context
Ambient context (permissions, store, user merged into every slot’s props) is planned but not wired up yet — today slot components receive only the slot-specific context listed below. Until it lands, read those values with the hooks instead:
if predicate: it receives the slot context, but not permissions — gate inside the component for now.
Page header slots
Rendered by<PageHeader> (@spree/dashboard-core/components/page-header.tsx) at the top of every detail and list page that uses the shared chrome.
page.actions
page.actions_dropdown
Page tabs slot
page.tabs (default)
Rendered by <PageTabs> at the right edge of any tabbed sub-nav.
Some pages override
slotName to scope tabs to a single resource (e.g., slotName="order.tabs"). The catalog will grow these as we wire them up; today only the default page.tabs name is in production use.
Detail-page slots
Every resource detail page renders a slot at the end of its sidebar column, and some also at the end of the main column. The slot name starts with the resource name, and the context key matches it. Host form. On pages marked host form: yes, the slot renders inside the page’s own<form> and exposes it — widgets can bind inputs via useHostForm() that hydrate, dirty-track, and save with the page’s Save button. The form key is what you register extension fields under in formFields. On every other page, widgets own their persistence; use useOptionalHostForm() to write a widget that adapts to both.
On host-form pages your widget renders inside the page’s
<form> element, so a <Button> in it submits the page unless you give it type="button".Products
product.form_main
product.form_sidebar
product.media.form_fields
product.media.actions
product.variant.form_fields
media.form_fields
media.context_menu
category.form_main
category.form_sidebar
collection.form_main
collection.form_sidebar
catalog.form_sidebar
price_list.form_sidebar
Orders and customers
order.form_main
order.form_sidebar
customer.form_main
customer.form_sidebar
company.form_main
company.form_sidebar
Company membership slots
The company’s member list and its “Add member” sheet carry three smaller slots, built for an extension that gives members roles.Promotions
promotion.form_sidebar
Inventory
purchase_order.form_main
purchase_order.form_sidebar
stock_transfer.form_main
stock_transfer.form_sidebar
Sellers (marketplace)
These slots are on the store owner’s seller management pages in the admin dashboard. For the seller’s own view, see the seller panel slots below.seller.form_main
seller.form_sidebar
seller_payout.form_main
seller_payout.form_sidebar
Settings
store.form_main
delivery_profile.form_main
delivery_profile.form_sidebar
webhook_endpoint.form_sidebar
Sheet slots
The create and edit sheets of the resources below render a slot after their built-in fields. Each sheet is a host form: a widget binds inputs withuseHostForm(), registers them under the form key (the slot’s prefix), and they save with the sheet’s own Create or Save button. The matching Admin API endpoint accepts what the model’s additional_permitted_attributes declares (see Adding an Attribute to a Core Resource).
Every sheet passes mode — 'new' or 'edit' — and its record, which is undefined while creating.
Staff invitations have no slot: the invitations endpoint deliberately accepts no extension attributes, because what it takes decides who gets access.
Seller panel slots
Rendered by the marketplace seller panel (@spree/seller-dashboard) rather than the admin dashboard, and registered the same way, in the seller panel’s own plugins. Their names start with seller. followed by the page — seller.form_main and seller.form_sidebar above belong to the admin dashboard instead.
The product page is a host form with the form key seller.product (seller.product.media and seller.product.variant for the rows its sheets edit): fields a widget binds with useHostForm() save with the product. The Seller API accepts only the attributes your extension declares in additional_seller_permitted_attributes — see Writable by sellers. The other seller panel pages have no page-wide form, so widgets there save via their own Seller API calls.
Sign-in slots
login.after
Nobody is signed in when this renders, so only call endpoints that need no session. Links to a public page fit here.
Replaceable slots
These slots render built-in content when nothing is registered. Registering a component replaces that content rather than adding to it.store_selection
Import the name as
STORE_SELECTION_SLOT from @spree/dashboard-core rather than typing it. Every sign-in, password reset and accepted invitation lands here, so the replacement owns both cases the default handles: a user with stores (link to /$storeId, or open one straight away with <Navigate>) and a user with none. The page renders on a blank canvas; wrap it in AuthShell from @spree/dashboard/components/spree/auth-shell to match the login page.
no_store_access
Import the name as
NO_STORE_ACCESS_SLOT from @spree/dashboard-core rather than typing it.
getting-started.task.<name>
<name> is the task’s name as the API returns it.
Editor slots (dynamic)
Promotions, price lists, and payment methods each pick their editor by type. The slot names are computed from the type’sapi_type shorthand, so registering against the right name is what hooks your editor in. The promotion and price list editor slots replace the built-in editor, which is generated from the type’s preference schema.
promotion.rule_form.<type>
promotion.action_form.<type>
price_list.rule_form.<type>
Payment method editor slots
Used by<PaymentMethodForm> to let payment-provider plugins replace pieces of the editor sheet. The slot names are computed per provider type (stripe, bogus, …), so registering against the right name is what hooks your editor in.
payment_method.guide.<providerType>
payment_method.form.<providerType>
payment_method.actions.<providerType>
PaymentMethodEditorContext
Adding a new slot
If a built-in page should expose a new injection point:- Pick a name (
<resource>.<area>, e.g.,order.timeline;seller.<page>.<area>in the seller panel) - Add
<Slot name="..." context={{ resource: order, /* ... */ }} />at the call site - If the slot sits inside the page’s form, make it a host form: wrap the form in
<FormProvider>, hydrate withextensionFormValues('<resource>', record), and mergeextensionSubmitValues('<resource>', form)into the save payload — the collection page is the reference - Document the slot here — host, intent, context shape, and form key
- Open the PR
Reference
<Slot>sourceslot-registry—registerSlot,removeSlot,updateSlot,useSlotEntries- Slots customization page — how to register against a slot

