Overview
Slot merges its props onto its immediate child element. This is useful for composition patterns where you want to pass props through to a child component while maintaining the child’s original props and ref.
Features
- Merges props with the child component
- Composes refs correctly
- Supports event handler composition
- Works with the
asChild pattern
Installation
npm install @radix-ui/react-slot
Anatomy
import { Slot } from '@radix-ui/react-slot';
export default () => (
<Slot>{/* child component */}</Slot>
)
API Reference
Merges its props onto its immediate child.
The child element to merge props onto. Must be a single React element.
Slottable
Used inside components that use Slot to indicate which content should be rendered as the slotted element.
Examples
Basic Usage
import { Slot } from '@radix-ui/react-slot';
const Button = ({ asChild, ...props }) => {
const Comp = asChild ? Slot : 'button';
return <Comp {...props} />;
};
// Usage
export default () => (
<div>
<Button onClick={() => console.log('clicked')}>Regular Button</Button>
<Button asChild>
<a href="/home">Link that looks like a button</a>
</Button>
</div>
);
With Custom Component
import { Slot } from '@radix-ui/react-slot';
const Card = ({ asChild, ...props }) => {
const Comp = asChild ? Slot : 'div';
return (
<Comp
{...props}
style={{
padding: 20,
borderRadius: 8,
border: '1px solid gray',
...props.style,
}}
/>
);
};
export default () => (
<Card asChild>
<article>
<h2>Article Title</h2>
<p>Article content...</p>
</article>
</Card>
);
Event Handler Composition
import { Slot } from '@radix-ui/react-slot';
const Button = ({ asChild, onClick, ...props }) => {
const Comp = asChild ? Slot : 'button';
const handleClick = (e) => {
console.log('Button clicked');
onClick?.(e);
};
return <Comp {...props} onClick={handleClick} />;
};
export default () => (
<Button
asChild
onClick={() => console.log('Child clicked')}
>
<a href="#">
Both handlers will be called
</a>
</Button>
);
With Slottable
import { Slot, Slottable } from '@radix-ui/react-slot';
const Button = ({ asChild, icon, children, ...props }) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp {...props}>
{icon}
<Slottable>{children}</Slottable>
</Comp>
);
};
export default () => (
<Button
asChild
icon={<span>→</span>}
>
<a href="/next">
Next Page
</a>
</Button>
);
How It Works
Slot works by:
- Taking a single child element
- Merging its own props with the child’s props
- Composing refs using
composeRefs
- Composing event handlers so both parent and child handlers are called
- Cloning the child element with the merged props
The asChild Pattern
The asChild pattern is commonly used with Slot to allow components to be polymorphic:
// Without asChild - renders a button
<Button onClick={...}>Click me</Button>
// With asChild - renders the child element (a link)
<Button asChild>
<a href="...">Click me</a>
</Button>
This pattern provides:
- Flexibility: Users can render any element while keeping your component’s functionality
- Accessibility: Users can choose the most semantically appropriate element
- Styling: The component’s styles are applied regardless of the underlying element
Common Use Cases
- Creating polymorphic components that can render as different elements
- Building composable primitives that pass props to children
- Implementing the
asChild prop pattern in component libraries
- Merging event handlers and refs in composite components
Limitations
- Only works with a single child element
- Child must be a valid React element, not a string or number
- Does not work with React.Fragment as the direct child