Skip to content

Prop types for children of slottable (asChild) components #895

Open
@DaniGuardiola

Description

@DaniGuardiola

Feature request

Overview

Public types representing the props that a slottable component will pass to its child when asChild={true}.

Examples in other libraries

N/A

Who does this impact? Who is this for?

Any user who wants to have better type-safety or that has to deal with non HTML-compatible components (different API than Radix is expecting for a child).

Additional context

My real-world example of this is the following:

  • We have a button component that is built with the react-aria usePress hook.
  • That hook replaces all pointer events (e.g. onMouseDown, onPointerUp, etc) with a custom, non-standard "press API" (e.g. onPressDown, onPress, etc).
  • We need to use that button as trigger for a dropdown menu (using asChild).
  • Radix passes onPointerDown, which doesn't exist on our button component, and therefore it gets ignored.

This was a tough one to debug, because we were not getting any type errors. With this type, we could do something like this:

// Radix type
export type RadixTriggerChildProps = {
  onPointerDown: (event: PointerEvent) => void,
  id: string,
  aria-whatever: string,
  // etc...
}

// user button
function CustomButton(props: ReactAriaProps) {
  const { buttonProps } = useButton(stuff)
  // etc...
}

// user trigger (erroring)
function CustomTrigger(props: TriggerChildProps) {
  return <CustomButton {...props} /> // ERROR!!!!1 (CustomButton doesn't accept onPointerDown)
}

// user trigger (correct)
function CustomTrigger({ onPointerDown: _, ...props }: TriggerChildProps) {
  // custom handling of incompatible props
  // probably by using controlled state to open the menu
  return <CustomButton {...props} onPress={() => setOpen(true)} />
}

// user dropdown menu
<>
  <RadixTrigger asChild>
    <CustomTrigger />
  </RadixTrigger>
</>

This improves quite a bit our current situation 😅 .

The specifics on how to implement the missing functionality are a different story, and that's something we want to get right to avoid hurting a11y and DX. I've got a few ideas, and I might come up with an additional (very minor) feature request that would enable a nice pattern. But that's for another day.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions