Open zxti opened 1 year ago
I'd recommend reading through our Advanced customization guide, in particular the section about consuming contexts. All of the contexts we provide are exported, so you can consume them in your own components. This makes it possible to reuse existing components you may have, either by modifying them to consume those contexts internally or by creating a small wrapper.
For buttons in particular, we do rely on the normalization done by React Aria hooks internally, so when a component contains a button it expects that this is happening. Therefore using an arbitrary element would not be enough, it would need to include the hooks too. But, if you want to customize the element you can drop down to the hook API yourself, and as long as it consumes from ButtonContext
it will work with other React Aria components. See this example from the button docs.
We've discussed APIs like as
and asChild
before, but we think they are too blunt an instrument. It's extremely powerful, but it's too easy to break the accessibility, behavior, interactions, or types of a component, and supporting this would prevent us from being able to make internal changes in the future. Usually there is a better way to solve individual problems that might have been solved by these tools in the past, such as sharing styles (reuse the same CSS class names), behavior, etc. But if there's something in particular you've come across that isn't already possible to solve please let us know.
I think it is worth to add asChild
prop. It is not necessary when designs are matching adobe's one. But react area components are supposed to be applied to various scenarios, even to those which were not built in into the library. Sometimes it is much easier and more flexible to use different component, but with the same functionality.
I've used as
prop in styled-components for a while, and find it less usable rather than asChild
. With asChild
you do not need to implement complex type inference, the props types are still simple as before. Also, with asChild
you can combine behavior of several components into one.
With asChild you do not need to implement complex type inference
asChild is not type safe, that's why it seems simpler. You can put any component in there even if it doesn't accept the right props or render the right element and it'll just be broken at runtime instead.
What are you trying to achieve? There's usually another way to do it. Happy to help if you have a specific case. Make sure to read the guide linked above as well.
@devongovett
asChild is not type safe, that's why it seems simpler. You can put any component in there even if it doesn't accept the right props or render the right element and it'll just be broken at runtime instead.
I feel that asChild
has the same level of type safety as as
prop. In the component in children of asChild
typescript still performs type checking as usual. Also, asChild
work better with generic components, if you use as
prop there can be difficulties with correct type inference of generic components in as
prop. And as
prop requires more complex typescript computations - I noticed that when I moved to asChild
approach autocompletion has become much faster
What are you trying to achieve? There's usually another way to do it. Happy to help if you have a specific case. Make sure to read the guide linked above as well.
Mostly for styling, I have several components that covers specific area of css - Flex, Grid, Stack, Paint, Text etc. And then combine styles that I need by using asChild
. I am just trying to avoid using all-in-one Box component, the same as giving control to all css properties to all components.
I feel that asChild has the same level of type safety as as prop.
In TypeScript you can't really specify the types of React children. Here's an example. As you can see, Parent
should only accept children with a foo
prop that is a string, but TypeScript actually allows any child with a different type, or even a random div that doesn't accept a foo
prop at all. That is why I say children are untyped, so therefore asChild
is untyped. Accepting arbitrary children can cause runtime errors, broken behavior, accessibility issues, and other problems.
Mostly for styling, I have several components that covers specific area of css - Flex, Grid, Stack, Paint, Text etc.
I think another approach to this is to share the styles rather than components. That way they can be reused across different components. This example uses inline styles but you could use tailwind, css-in-js, etc. to achieve the same result.
function flex(options) {
return {display: 'flex', gap: options.gap, flexDirection: options.direction, /* ... */ };
}
<ReactAriaComponent style={flex({...})} />
This approach is more granular - you are explicit about what is being shared (only styles, no events, or other hidden behaviors), and you have control over how multiple of these are merged together (with asChild you don't have control over how props from multiple components are merged). This also makes it easier to share styles between different raw DOM elements too, rather than being locked into a <div>
being rendered by <Flex>
or needing an as
/asChild
prop there too.
Another way to think about this is that components should be responsible for the behavior, semantics, and elements that are rendered, and styling should be passed into them. The component knows what is represents โ a button, checkbox, switch, etc. โ so it needs to control the rendered elements and behaviors. asChild
flips this around so behaviors get passed down to arbitrary elements (which may also have other conflicting behaviors of their own), easily resulting in unexpected issues and bugs. Passing styles into components avoids these issues and results in more predictable behavior and fewer potential accessibility issues.
@devongovett thanks for detailed explanation.
I wanted to emphasize that asChild
prop is better than as
prop approach in my opinion. But, yes there is no way to type react children. I just think that asChild
can be used for composition because of server components support.
Also about passing functions to style components, I thought I tried this approach, but after that all my JSX turned into div
s and span
s what was harder to read than named jsx tags like Flex
I recently ran into issues styling the <CalendarCell/>
. Its rendering a <td><div>
and there is no way to target the <td>
or give it any attributes or classnames.
Having worked for many months on a design system based on RAC I can say not having access to the wrapper is the probably the number 1 friction point I experienced. In order to allow the behavior I was left with forking RAC or copying individual components, causing all sorts of issues with component contexts or utils not being exported.
I like the renderProps
pattern in react-aria-component but it would be awesome if components allow you to opt out of the wrapper being rendered and have the HTML attributes be passed to the renderProps's object. This could be introduced as a non breaking change as far as I can see.
Provide a general summary of the feature here
Radix UI supports the asChild prop, which lets you provide a complete other component to serve in a certain role.
This would let you much more easily adopt React Aria Components within codebases that already have other components (but also some gotchas that users just have to be aware of). Especially for things like Buttons, etc.
๐ค Expected Behavior?
See the Radix docs: https://www.radix-ui.com/primitives/docs/guides/composition
๐ฏ Current Behavior
You are limited to just using React Aria Components with itself.
๐ Possible Solution
No response
๐ฆ Context
Has been brought up as feedback in the past:
https://github.com/adobe/react-spectrum/issues/2331#issuecomment-920957954
https://news.ycombinator.com/item?id=35853692
๐ป Examples
No response
๐งข Your Company/Team
No response
๐ท Tracking Issue
No response