use-slots
use-slots copied to clipboard
Slot placement for React
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. In most cases these children should also 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 would be wrong, since it depends on the order of the components.
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 child.");
return (
<dialog>
{slots.modalHeader ? slots.modalHeader : null}
{slots.modalBody}
</dialog>
);
}
License
MIT