Tutorial
Slot Element and hasSlot

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.