Slot

A utility component that merges its props onto its immediate child.

Source Code

import { cn } from "@/lib/utils";
import { Children, cloneElement, isValidElement } from "react";
 
const isValidSlottableElement = (
  value: unknown
): value is React.ReactElement<{
  children?: React.ReactNode;
  className?: string;
  style?: React.CSSProperties;
}> => {
  return (
    isValidElement(value) &&
    !!value.props &&
    typeof value.props === "object" &&
    "key" in value
  );
};
 
export const Slot = ({
  children,
  ref,
  ...props
}: React.ComponentPropsWithRef<React.ElementType>) => {
  const element = Children.only(children);
 
  if (isValidSlottableElement(element)) {
    return cloneElement(element, {
      ...props,
      ...element.props,
      ref,
      style: {
        ...props.style,
        ...element.props.style,
      },
      className: cn(element.props.className, props.className),
    });
  }
 
  throw new Error("Slot needs a valid react element child");
};
 
Slot.displayName = "Slot";
 
type SlottableProps = {
  asChild: boolean;
  child: React.ReactNode;
  children: (child: React.ReactNode) => React.ReactElement;
};
 
/**
 * Slottable is required when you want to use a Slot but render more than one child inside (e.g.: a button with the children + a spinner icon)
 *
 * see https://github.com/radix-ui/primitives/issues/1825
 */
export const Slottable = ({
  asChild,
  child,
  children,
  ...props
}: SlottableProps) => {
  return (
    <>
      {asChild
        ? isValidSlottableElement(child)
          ? cloneElement(child, props, children(child.props.children))
          : null
        : children(child)}
    </>
  );
};

Anatomy

<Slot>
  <component />
</Slot>

Features

  • Prop Merging: Automatically merges props from the Slot onto its child element
  • Style Composition: Combines styles from both Slot and child element
  • ClassName Merging: Intelligently merges classNames using the cn utility
  • Ref Forwarding: Properly forwards refs to the child element
  • Type Safety: Full TypeScript support with proper type inference

API Reference

Slot

PropDefaultTypeDescription

children

*
-

ReactElement

A single React element to merge props onto.

ref

-

React.Ref<any>

A ref to forward to the child element.

Slottable

A utility component for handling multiple children in Slot-based components.

PropDefaultTypeDescription

asChild

*
-

boolean

Whether to render the child as a Slot.

child

*
-

ReactNode

The child element to render.

children

*
-

(child: ReactNode) => ReactElement

Render function that receives the child and returns a React element.

Examples

Basic Usage

Merging props onto a button element.

<Slot onClick={() => alert("clicked")} data-foo="bar">
  <button>Click me</button>
</Slot>
 
/**
 * Renders:
 *  <button data-foo="bar" onClick={() => alert("clicked")}>
 *   Click me
 *  </button>
 */

Using Slottable

Handling multiple children with the Slottable component.

<Slottable asChild child={<button>Click me</button>}>
  {(child) => (
    <>
      <span>sufix</span>
      {child}
      <span>prefix</span>
    </>
  )}
</Slottable>
 
/**
 * Renders:
 *  <button>
 *   <span>sufix</span>
 *   click me
 *   <span>prefix</span>
 *  </button>
 */

Common Use Cases

  • Creating polymorphic components
  • Building flexible UI components that can render as different elements
  • Implementing component composition patterns

Technical Details

The Slot component uses React's cloneElement to merge props onto its child element. It handles:

  • Prop spreading
  • Style composition
  • ClassName merging
  • Ref forwarding
  • Type safety

The implementation ensures that props from the Slot take precedence over the child's props, while still preserving the child's original functionality.