use-slots

Slot placement for React

Usage no npm install needed!

<script type="module">
  import useSlots from 'https://cdn.skypack.dev/use-slots';
</script>

README

use-slots 🧩

Slot placement for React

The Problem

When you use Compound Components Pattern you need to guarantee that the children you receive are the same as you expect and in most cases these children should follow some order, for example:

import { Modal, ModalHeader, ModalBody } from "some-react-modal-lib";

export function Prompt() {
  return (
    <Modal>
      <ModalHeader>Content Header</ModalHeader>
      <ModalBody>Content Body<ModalBody>
    </Modal>
  )
}

In this case, if you put ModalHeader, after ModalBody the render result could be wrong because it depends on the order.

Installation

npm install --save use-slots

Usage

// import these functions
import { beSlot, useSlots } from "use-slots";

// create your component as usual
function ModalHeader({ children }) {
  return <header>{children}</header>;
}

// enhance your component with beSlot giving a name for your slot
const SlottedModalHeader = beSlot(ModalHeader, "modalHeader");

function ModalBody({ children }) {
  return <div>{children}</div>;
}

const SlottedModalBody = beSlot(ModalBody, "modalBody");

function Modal({ children }) {
  // pass children to useSlots hook and use returned object instead of children
  const slots = useSlots(children);

  return (
    <dialog>
      {slots.modalHeader}
      {slots.modalBody}
    </dialog>
  );
}

You can also combine use-slots with tiny-invariant to provide descriptive errors in development. In this example we presume that ModalHeader is optional and ModalBody is required.

import invariant from "tiny-invariant";

function Modal({ children }) {
  const slots = useSlots(children);

  invariant(!slots.modalBody, "You should pass ModalBody as a children.");

  return (
    <dialog>
      {slots.modalHeader ? slots.modalHeader : null}
      {slots.modalBody}
    </dialog>
  );
}

License

MIT