npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@haitch-ui/react-slot

v0.1.2

Published

A strict, compositional Slot component for React that safely forwards props, events, and refs to exactly one valid child element.

Readme

Slot

Note - this package is now deprecated. Future releases will be included in @haitch-ui/react

A strict, compositional slot component for React that safely forwards props, events, and refs to exactly one valid child element.

Designed for design systems and headless UI components where you want to enhance or wrap a child element without adding extra DOM nodes, while keeping behavior predictable and explicit.


Table of Contents


Installation

npm i @haitch-ui/react-slot

Why Slot Exists

In many component libraries, you want to:

  • Attach props to user-provided elements
  • Avoid extra wrappers (div, span, etc.)
  • Preserve refs and event handlers
  • Maintain strict control over structure

Slot enforces exactly one valid React element child and clones it with deterministic, well-defined merging rules.


Core Guarantees

Slot guarantees:

  1. Exactly one non-whitespace child
  2. Child must be a valid React element
  3. Fragments are explicitly disallowed
  4. Props are merged deterministically
  5. Refs are safely composed
  6. Event handlers are composed, not overridden
  7. Whitespace-only JSX formatting is ignored

If any invariant is violated, Slot throws early with a clear error.


Basic Usage

<Slot className="btn-primary" onClick={handleClick}>
  <button className="btn">Save</button>
</Slot>

Resulting behavior:

  • className"btn btn-primary"
  • onClick → child handler runs first, then slot handler
  • No wrapper elements added
  • Refs still point to the <button>

Child Requirements

Allowed

<Slot>
  <button />
</Slot>
<Slot>
  {"\n  "}
  <button />
  {"\n"}
</Slot>

Whitespace-only text nodes are ignored.


Disallowed

No child

<Slot />

Multiple children

<Slot>
  <button />
  <button />
</Slot>

Non-element child

<Slot>{"hello"}</Slot>
<Slot>{123}</Slot>

Fragment

<Slot>
  <>
    <button />
  </>
</Slot>

Fragments are rejected because props and refs cannot be safely attached.


Prop Merging Rules

Default Behavior

  • Slot props win over child props
  • Child props are preserved unless explicitly overridden
<Slot id="slot-id">
  <button id="child-id" />
</Slot>

Result:

<button id="slot-id" />

className

  • Concatenated if both exist
  • Order: child first, slot second
<Slot className="slot">
  <button className="child" />
</Slot>

Result:

"child slot"

style

  • Shallow merge
  • Slot values win on conflicts
<Slot style={{ color: "red" }}>
  <button style={{ color: "blue", margin: 4 }} />
</Slot>

Result:

{
  color: "red",
  margin: 4
}

Event Handler Composition

Supported events are composed, not overridden:

  • onClick
  • onMouseDown / Up
  • onPointerDown / Up
  • onMouseEnter / Leave
  • onFocus / Blur
  • onKeyDown / Up

Execution Order

  1. Child handler
  2. Slot handler
<Slot onClick={() => console.log("slot")}>
  <button onClick={() => console.log("child")} />
</Slot>

Output:

child
slot

If only one side provides a handler, it is used unchanged.


Ref Behavior

Refs are safely composed using @haitch-ui/react-compose-refs.

Supported ref types:

  • Object refs
  • Callback refs
  • Forwarded refs

All refs receive the same underlying DOM element.

const ref = useRef<HTMLButtonElement>(null);

<Slot ref={ref}>
  <button />
</Slot>

Intentional Constraints

These are deliberate design decisions:

| Constraint | Reason | | -------------------- | -------------------------------------- | | Single child only | Prevents ambiguous prop application | | No fragments | Fragments cannot receive refs or props | | No implicit wrapping | Preserves DOM structure | | Early runtime errors | Fail fast, easier debugging |


Testing Philosophy

The test suite validates behavioral guarantees, not implementation details.

Covered cases include:

  • Child validation rules
  • Whitespace handling
  • Prop precedence
  • className and style merging
  • Event handler composition
  • Ref composition (object + callback)
  • Pass-through of data-* and aria-* props

Tests use:

  • Vitest
  • React Testing Library
  • @testing-library/jest-dom

When to Use Slot

Use Slot when:

  • Building headless or unstyled components
  • Designing APIs that accept “any element”
  • You need strict structural guarantees
  • Predictable prop + ref behavior matters

Avoid Slot when:

  • Multiple children are required
  • Fragments are unavoidable
  • You want implicit wrapping

API Reference

<Slot />

const Slot: React.ForwardRefExoticComponent<
  {
    children: React.ReactNode;
  } & Record<string, unknown> &
    React.RefAttributes<HTMLElement>
>;

Props

children (required)

children: React.ReactNode;
  • Must resolve to exactly one non-whitespace React element

  • Whitespace-only text nodes are ignored

  • The following are not allowed:

    • No children
    • Multiple non-whitespace children
    • Strings, numbers, or other primitives
    • React.Fragment

Violations throw a runtime error.


Forwarded Props

All additional props are forwarded to the child:

<Slot disabled aria-label="Save">
  <button />
</Slot>

Becomes:

<button disabled aria-label="Save" />

Prop precedence

  • Slot props override child props by default
  • Exceptions: className, style, event handlers

Event Handlers

If both child and slot define a handler:

(event) => {
  childHandler(event);
  slotHandler(event);
};

If only one exists, it is used unchanged.


ref

ref?: React.Ref<HTMLElement>;
  • forwardRef enabled
  • Child and slot refs are composed
  • All refs receive the same element instance

Runtime Errors

| Condition | Error | | ----------------- | ------------------------------------------------ | | No valid child | Received 0 non-whitespace children | | Multiple children | Received N non-whitespace children | | Non-element child | expects a single valid React element child | | Fragment child | must be a single element, not a React.Fragment |


TypeScript Notes

  • Slot does not infer child prop types
  • This is intentional for headless flexibility
  • Consumers should rely on the child element’s typing

Summary

Slot is intentionally strict.

That strictness makes it:

  • Predictable
  • Safe
  • Design-system ready
  • Easy to reason about

If it renders, it behaves exactly how you expect.