Modern, next-gen React utilities for composing slots and props. Built for headless and compound component patterns with predictable prop merging.
React components often need to support custom rendering of their internal elements. While passing a component prop or using a render prop works, it often leads to complex prop drilling and breaks the natural JSX structure.
The slot pattern allows you to pass a child element that "merges" with the component's internal element. This provides a clean API for users to customize the underlying DOM element or component while preserving the behavior and styling provided by the parent. This library provides the foundational utilities to implement this pattern reliably.
- Predictable Prop Merging: Intelligently merges class names, styles, and event handlers.
- Ref Composition: Automatically composes multiple refs into a single callback ref.
- Slottable Support: Allows wrapping specific parts of children to be used as the slot target.
- Higher-Order Components: Utilities for setting default props and class names.
- Type Safe: Built with TypeScript for excellent developer experience.
bun add react-slot-utils
# or
npm install react-slot-utilsimport { Slot } from 'react-slot-utils';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
}
function Button({ asChild, ...props }: ButtonProps) {
const Component = asChild ? Slot : 'button';
return <Component {...props} className="btn-base" />;
}
// Usage: renders as an <a> tag but with "btn-base" class and button behaviors
export const App = () => (
<Button asChild>
<a href="/home">Home</a>
</Button>
);import { Slot, Slottable } from 'react-slot-utils';
function Card({ children, ...props }) {
return (
<Slot {...props}>
<div className="card-wrapper">
<Slottable>{children}</Slottable>
<span className="decoration">★</span>
</div>
</Slot>
);
}A component that renders its child and merges its own props onto that child.
- If a
Slottablechild is found, it uses theSlottable's children as the primary element. - Merges
classNameusingclassnames. - Merges
styleobjects (shallow merge). - Composes event handlers (child handler runs first).
- Composes
refs.
interface SlotProps extends HTMLAttributes<HTMLElement>, PropsWithChildren {
ref?: Ref<HTMLElement>;
}A utility component used inside Slot to mark which part of the children should be treated as the element to be cloned.
Merges two sets of props with specific logic:
- Event Handlers: Both handlers are called. The child handler executes first. If the child handler calls
event.preventDefault(), the slot handler is not called. - Styles: Shallow merges style objects. Slot styles are spread first, child styles override.
- ClassNames: Merges strings using the
cnutility. - Other Props: Child props override slot props.
A HOC that returns a new component with the specified default props applied.
function withDefaultProps<P extends object, K extends keyof P>(
Component: ComponentType<Pick<P, K> & Omit<P, K>>,
defaultProps: Pick<P, K>,
): FC<Omit<P, K>>A version of withDefaultProps for components with complex generic types.
Renders a React element with additional props merged in.
A HOC that prepends default class names to the component's className prop.
function withDefaultClassNames<P extends { className?: string }>(
Component: ComponentType<P>,
defaultClassNames: string,
): FC<ClassNameDefaultize<P>>A re-export of the classnames utility for conditional class merging.
Sets the displayName of a component and returns it.
Composes multiple React refs (function or object) into a single callback ref.
Flattens React fragments and nested arrays into a flat array of ReactNode.
Type guard to check if a child is a Slottable component.
Type guard to check if a value is a React Ref.
Type guard to check if a value is a CSSProperties object.
Checks if a prop name starts with on followed by an uppercase letter.
MIT © Taeyeong Kim