0.2.0 • Published 7 months ago

named-slots v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

named-slots 🦥

Slots for preact, react and solid in under 0.2Kb, unzipped, unminified

npm i named-slots

Use declarative "holes" in your components with <Slot name="header"> instead of an imperative prop based approach. Inspired by slots in Vue/Svelte/Angular/WebComponents.

Fill them with any Component or HTML element in the slot with the data-slot attribute: <div data-slot="header">.

See example below.

Usage

Declaring slots in component where they render, eg a Card component

Slot props
namename of the slot
fromalways the children of where the Slot is defined, to give the Slot control over it
children/contentFallback content for when nothing has been slotted in. If nothing is slotted, and no fallback is given, it will not render at all
// Card.tsx
import { Slot, Slottable } from "named-slots";

export const Card = ({ children }: { children: Slottable }) => {
  // optionally validate slots during development, see below
  return (
    <div className={"card"}>
      {/* header has no fallback, renders only if slot is provided */}
      <Slot name="header" from={children} />
      <Slot name="content" from={children}>
        Fallback content
      </Slot>
      <div className={"class-from-inside"}>
        <Slot name="footer" from={children}>
          Fallback footer
        </Slot>
      </div>
    </div>
  );
};

Then get consumed like this:

// Page.tsx
import { Slot } from "named-slots";

<Card>
  <div data-slot="header" className={"class-from-outside"}>
    This div is not semantic
  </div>
  <JsxComponent data-slot="content" />
  <footer data-slot="footer">I'm a footer</footer>
</Card>;

This will render the following html:

<div class="card">
  <div data-slot="header" class="class-from-outside">
    This div is not semantic
  </div>
  <section class="preact">Some content</section>
  <div class="class-from-inside">
    <footer data-slot="footer">I'm a footer</footer>
  </div>
</div>

Solid.js

Since solid does not use a VDOM the usage changes a tiny bit, with a dedicated import.

import { Slot } from "named-slots/solid";

In addition every element with data-slot needs to be an HTML element, not a Solid component (or wrapped in one). In the example above, <JsxComponent data-slot="content" /> would not work. <div ="content"><JsxComponent data-slot="content" /></div>.

Validation and debugging. Almost type-safety.

Adding types or autocompletion to validate the slot name has been a miserable failure so far.

Runtime validation can be achived using validateSlots. This will be stripped away automatically in production (based on process.env.NODE_ENV) to not impact bundle size.

import { Slot, validateSlots, type Slottable } from "named-slots";

export const Card = ({ children }: { children: Slottable }) => {
  validateSlots(children, ["header", "content", "footer"], {
    inComponent: Card,
  });
  return (...)
};

Made with 🍕 in Amsterdam.

0.2.0

7 months ago

0.1.0

8 months ago

0.0.1

8 months ago