Skip to content

Toolbar Slots

The slot system lets any component in the React tree inject toolbar items into a named slot without prop drilling. The slot host (a ToolbarGroup or a ToolbarContext) declares the slot name; any number of independent contributors can register items into it. The registry is managed by a ToolbarSlotProvider that must wrap both the toolbar and the contributors.

Wrap the relevant part of the tree in a ToolbarSlotProvider, give a ToolbarGroup a slotName, and use ToolbarSlot anywhere inside the provider to inject items:

import { ToolbarSlotProvider, ToolbarSlot } from '@cratis/components/Toolbar';
export const MyCanvas = () => (
<ToolbarSlotProvider>
<Toolbar>
<ToolbarGroup slotName='canvas-tools'>
<ToolbarButton icon='pi pi-pencil' title='Draw' />
</ToolbarGroup>
</Toolbar>
{/* The feature injects its own button — no prop drilling */}
<ToolbarSlot slotName='canvas-tools' order={10}>
<ToolbarButton icon='pi pi-star' title='Feature: Favorite' />
</ToolbarSlot>
<MyFeatureComponent />
</ToolbarSlotProvider>
);

Creates the slot registry and makes it available to the rest of the tree. Place it as high as needed so that both the toolbar and all contributing components fall inside it.

<ToolbarSlotProvider>
{children}
</ToolbarSlotProvider>

Renders nothing in the DOM — its only purpose is to register its children into the named slot.

<ToolbarSlot slotName='my-slot' order={5}>
<ToolbarButton icon='pi pi-cog' title='Settings' />
</ToolbarSlot>

The component registers on mount and unregisters on unmount, so conditional rendering works naturally.

PropTypeDefaultDescription
slotNamestringrequiredThe slot to inject into
ordernumber0Determines render position relative to other slot items — lower values appear first
childrenReactNoderequiredThe toolbar item(s) to inject

Both ToolbarGroup and ToolbarContext can act as slot hosts via the slotName prop. Injected items are appended after the host’s own children, sorted by order.

<ToolbarSlotProvider>
<Toolbar>
<ToolbarGroup slotName='drawing'>
<ToolbarButton icon='pi pi-pencil' title='Draw' />
</ToolbarGroup>
</Toolbar>
<ToolbarSlot slotName='drawing' order={20}>
<ToolbarButton icon='pi pi-minus' title='Line' />
</ToolbarSlot>
</ToolbarSlotProvider>

Items injected into a ToolbarContext slot are only visible when that context is active. The slot items participate in the context’s fade-in/out animation.

<ToolbarSlotProvider>
<Toolbar>
<ToolbarSection activeContext={currentContext}>
<ToolbarContext name='drawing' slotName='drawing-extras'>
<ToolbarButton icon='pi pi-pencil' title='Draw' />
</ToolbarContext>
</ToolbarSection>
</Toolbar>
<ToolbarSlot slotName='drawing-extras' order={5}>
<ToolbarButton icon='pi pi-star' title='Injected tool' />
</ToolbarSlot>
</ToolbarSlotProvider>

Any number of independent components can register into the same slot. They are rendered in ascending order:

<ToolbarSlotProvider>
<Toolbar>
<ToolbarGroup slotName='shared' />
</Toolbar>
{/* order 5 — appears first */}
<ToolbarSlot slotName='shared' order={5}>
<ToolbarButton icon='pi pi-heart' title='Likes' />
</ToolbarSlot>
{/* order 10 — appears second */}
<ToolbarSlot slotName='shared' order={10}>
<ToolbarButton icon='pi pi-star' title='Favorites' />
</ToolbarSlot>
{/* order 20 — appears last */}
<ToolbarSlot slotName='shared' order={20}>
<ToolbarButton icon='pi pi-bell' title='Notifications' />
</ToolbarSlot>
</ToolbarSlotProvider>

If you need to read slot items directly (for example when building a custom slot host), call useToolbarSlot:

import { useToolbarSlot } from '@cratis/components/Toolbar';
const MyCustomHost = ({ slotName }: { slotName: string }) => {
const slotItems = useToolbarSlot(slotName);
return <div className='my-host'>{slotItems}</div>;
};

The hook uses useSyncExternalStore internally and only triggers re-renders when the slot’s content actually changes.