HomeAbout Me

Advanced React Patterns: Slots

By Daniel Nguyen
Published in React JS
July 05, 2025
2 min read
Advanced React Patterns: Slots

🎛️ Mastering the Slots Pattern in React for Component Flexibility

One-liner: The Slots Pattern allows components to take on specific roles within a parent’s layout, enabling flexible composition and safer, accessible design.

🧱 Problem: Balancing Flexibility and Correctness in UI Libraries

When building a component library, you’re constantly balancing two goals:

  1. Correctness – accessibility, semantic markup, behavior.
  2. Flexibility – giving developers the freedom to structure their UI however they want.

How can we ensure developers don’t break important things (like accessibility), but still offer composability?

That’s where Slots come in. 🎯


🤔 What Are Slots?

Slots allow child components to declare their role within a parent component. The parent can then provide role-specific props through context—without controlling layout or markup structure.

Real-World Analogy

In HTML, consider this form element:

<CheckboxGroup>
<Label>Pets</Label>
<MyCheckbox value="dogs">Dogs</MyCheckbox>
<MyCheckbox value="cats">Cats</MyCheckbox>
<Text slot="description">Select your pets.</Text>
</CheckboxGroup>

The Text component has slot="description", telling the parent:

“Hey, I’m the description.”

Then CheckboxGroup supplies the appropriate props—like id and aria-describedby—based on the slot name.

This pattern is widely used in libraries like react-aria, and it’s fantastic for decoupling layout from logic, enabling accessibility, and promoting reusability.


🛠️ Implementing the Slot Pattern

Here’s how we do it in React:

Step 1: Provide Slot Props via Context

Your parent component (e.g. TextField) generates the data and passes it through context.

function TextField({ children }) {
const id = useId()
const slots = {
label: { htmlFor: id },
input: { id },
}
return (
<SlotContext.Provider value={slots}>
{children}
</SlotContext.Provider>
)
}

Step 2: Consume Slot Props in Children

A hook merges the provided props with the default/slot-specific ones:

function useSlotProps(props, slotName) {
const slotContext = useContext(SlotContext)
const slotProps = slotContext?.[slotName] ?? {}
return { ...slotProps, ...props }
}

Now your components can declare which role they serve:

function Label(props) {
const labelProps = useSlotProps(props, 'label')
return <label {...labelProps} />
}
function Input(props) {
const inputProps = useSlotProps(props, 'input')
return <input {...inputProps} />
}

✅ Accessibility props are applied ✅ Layout is customizable ✅ Logic is abstracted


📦 Real-World Benefits

Let’s look at a real use case.

Problem: Manual IDs = Mistakes

Developers often forget to manually associate <label> and <input> using id and htmlFor, which breaks accessibility.

Using the slots pattern, your TextField component can handle the logic behind the scenes:

<TextField>
<Label>Email</Label>
<Input />
</TextField>

Behind the scenes, Label and Input receive automatically generated id and htmlFor props—ensuring correctness without extra work.


⚡ Going Further: Generic Slot Components

Let’s make our API even more powerful.

Before:

You might have separate components like:

<Toggle>
<ToggleOn>Party time 🎉</ToggleOn>
<ToggleOff>Sad town 😭</ToggleOff>
<ToggleButton />
</Toggle>

Now let’s unify this using generic components + slots:

After:

<Toggle>
<Label>Party mode</Label>
<Switch />
<Text slot="onText">Let’s party 🥳</Text>
<Text slot="offText">Sad town 😭</Text>
</Toggle>

The Toggle parent passes slot-specific props:

const slots = {
label: { htmlFor: switchId },
switch: { id: switchId, on: isOn, onClick: toggle },
onText: { hidden: !isOn },
offText: { hidden: isOn },
}

Your Text and Switch components handle the rest:

function Text({ slot, ...props }) {
const textProps = useSlotProps(props, slot)
return <span {...textProps} />
}
function Switch({ slot, ...props }) {
const switchProps = useSlotProps(props, slot)
return <button {...switchProps}>{switchProps.on ? 'ON' : 'OFF'}</button>
}

No more manually passing state. No custom hooks. No prop drilling. Just clean, declarative composition. ✨


🔄 Bonus: Removing Boilerplate

Once this pattern is in place:

  • You can delete repetitive components (ToggleOn, ToggleOff, ToggleButton)
  • Reduce bugs from forgetting to wire up state or props
  • Give developers full control over structure and styling

It’s especially helpful when building design systems, accessibility wrappers, and framework-agnostic UI kits.


🧪 Final Thoughts

The Slots Pattern is a step up from compound components:

FeatureCompound ComponentsSlots Pattern
State SharingContextContext
Layout FlexibilityModerateVery High
CustomizationLimited (custom API)Unlimited (native props)
Accessibility SupportManualBuilt-in

If you’re building component libraries (especially reusable or accessible ones), slots are your secret weapon.


🔗 Resources



Tags

#ReactPatterns

Share

Previous Article
Advanced React Patterns: Compound Components

Table Of Contents

1
🧱 Problem: Balancing Flexibility and Correctness in UI Libraries
2
🤔 What Are Slots?
3
🛠️ Implementing the Slot Pattern
4
📦 Real-World Benefits
5
⚡ Going Further: Generic Slot Components
6
🔄 Bonus: Removing Boilerplate
7
🧪 Final Thoughts
8
🔗 Resources

Related Posts

Advanced React Patterns: Control Props
July 09, 2025
2 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media