create-slots
Bring Slots to React, with SSR support
Checkout the slides for the background story
Usage
Simple version (only one slot is used per slot type)
- Create your component with slots
import React, { useId } from 'react'
import { createHost, createSlot } from 'create-slots'
const FieldLabel = createSlot('label')
const FieldInput = createSlot('input')
const FieldDescription = createSlot('div')
type FieldProps = React.ComponentPropsWithoutRef<'div'>
export const Field = (props: FieldProps) => {
const id = useId()
return createHost(props.children, (Slots) => {
const labelProps = Slots.getProps(FieldLabel)
const inputProps = Slots.getProps(FieldInput)
const inputId = inputProps?.id || id
return (
<div {...props}>
{labelProps && <label {...labelProps} htmlFor={inputId} />}
<input id={id} {...inputProps} />
{Slots.get(FieldDescription)}
</div>
)
})
}
Field.Label = FieldLabel
Field.Input = FieldInput
Field.Description = FieldDescription
- Use it
<Field>
<Field.Description>Order doesn't matter</Field.Description>
<Field.Input id="custom-id" />
<Field.Label>I'll use "custom-id"</Field.Label>
</Field>
React Slots RFC with utils)
List slots (fully implemented theimport React, { useState } from 'react'
import { createHost, createSlot, getSlotProps, isSlot } from 'create-slots/list'
const SelectItem = createSlot('li')
const SelectDivider = createSlot('hr')
type SelectProps = React.ComponentPropsWithoutRef<'ul'>
export const Select = (props: SelectProps) => {
const [selected, setSelected] = useState<string>()
const indexRef = React.useRef(0)
return (
<div>
<div>Selected: {selected ?? ''}</div>
{createHost(props.children, (slots) => {
indexRef.current = 0
return (
<ul {...props}>
{slots.map((slot) => {
if (isSlot(slot, SelectItem)) {
const slotProps = getSlotProps(slot)
return (
<li
{...slotProps}
role="option"
data-index={indexRef.current++}
aria-selected={slotProps.value === selected}
onClick={() => setSelected(slotProps.value as string)}
/>
)
}
return slot
})}
</ul>
)
})}
</div>
)
}
Select.Item = SelectItem
Select.Divider = SelectDivider
- Use it
<Select>
<Select.Item value="foo">Foo</Select.Item>
<Select.Divider />
<Select.Item value="bar">Bar</Select.Item>
</Select>
License
MIT © Neo Nie