Slot Elements and hasSlot
Object
Slot elements come with some convenient built-in functionality. On this page,
we'll see how react-slots
handles common patterns when dealing with a
slot-based architecture.
Specifying Fallback Content:
Sometimes, you want to render a fallback in a slot even if the parent doesn't provide content for that slot. To achieve this, simply include your fallback content as children of the slot element.
Consider a listIcon
slot for the BulletList
component. We might want to
display a fallback list icon if the parent didn't provide a unique icon for this
slot.
function BulletList({ children, items }) {
const { slot } = useSlot(children);
return (
<ul>
{items.map((item) => (
<li>
{/* This emoji will be rendered as a fallback for the listIcon slot */}
<slot.listIcon>👉</slot.listIcon>
{item}
</li>
))}
</ul>
);
}
The hasSlot Object
type hasSlot = {[slotName: string]: true | undefined}
.
hasSlot is an object with keys representing slot names specified by the parent.
For example, if a parent provides a foo
slot, then hasSlot.foo
in the child
component will be true; otherwise, undefined. You can access hasSlot by
destructuring the useSlot return value.
hasSlot is useful when you want to style or structure the UI based on whether the parent provided content for a particular slot.
For example, the title
slot for the Card component might render a horizontal
line after the card title only if the title is provided:
function Card() {
const { slot, hasSlot } = useSlot();
return (
<div>
<slot.title />
{hasSlot.title && <hr />}
...
</div>
);
}
Passing State Up to a Parent
If you want a parent to access an internal state just specify it on the slot
element and parent will receive it. The following ToggleButton
component keeps
track of the isToggled
state and passes it up to the parent so that the parent
can change the text on the button based on whether the button is toggled or not.
As a fallback, it renders "Enabled" and "Disabled" if the parent doesn't provide
custom text.
function ToggleButton({ children }) {
const { slot } = useSlot(children);
const [isToggled, setIsToggled] = useState(false);
return (
<button onClick={() => setIsToggled(!isToggled)}>
{/* Pass the isToggled prop up to the parent*/}
<slot.default isToggled={isToggled}>
{/* Dynamic fallback content */}
{isToggled ? "Enabled" : "Disabled"}
</slot.default>
</button>
);
}
We'll see how to access the isToggled
state from the parent in the
templates section.