Consider upgrading to a component-based API #2368
Replies: 18 comments 24 replies
-
Hey, thanks for the feedback! React Aria is certainly low level. Our goal when releasing it was that it could accommodate the widest possible number of use cases. This includes the ability to fully customize the appearance, DOM structure, behavior, etc. We aim to let you have just as much control as if you were building the component from scratch with the raw DOM APIs as you might have before, but also provide a lot of behavior and a11y for you. Our hope was that higher level libraries like Headless UI would be built on top of React Aria. It's only possible to do this one way: the lower level hooks have to come first. We did look at component and render-props based APIs when designing React Aria. In fact, we started thinking about this problem long before Hooks even existed. However, we never found the right way to do this that offered enough customization potential. Our goal was to build something that we could build React Spectrum (our design system) off of, and I don't think we could have done that without lower level access in some areas. A few considerations:
That being said, I do think there is a lot we can do to improve here without compromising on these goals. We've started at the lowest level layer possible, and are slowing working our way up the stack to provide higher level abstractions. Here are some ideas we have been thinking about:
Overall, the goal should be to reduce the number of different hooks you need to call for common cases, while retaining flexibility to drop down to lower level APIs where needed. We don't currently have plans to offer an official component-based wrapper around our hooks, mainly for lack of time, but we would encourage and assist the community in such an effort. Personally, I would love to see multiple libraries with different takes on such an API built on top of React Aria, e.g. integrating with or optimizing for specific styling methodologies (e.g. tailwind, css-in-js, etc.). If you or anyone else is interested in working on such a library, we'd be happy to assist in any way we can. 😄 |
Beta Was this translation helpful? Give feedback.
-
Hey @devongovett, thanks for the thoughtful response! I totally agree that hooks provide more control for certain edge cases where you need to get really custom (although some of those examples are solvable by components). I think hooks would still be key as the tiny building blocks, but an additional component-based layer would solve 90% of use cases without the current complexity. It's hard to overstate how much simpler the Headless UI approach is. Even if this library is for UI kits, I'd be willing to bet that a lot of people come to it when first implementing a specific component that isn't well handled by browser defaults. (In my case this was trying to add an autocomplete component to a fairly simple UI kit.) Totally makes sense if it's a lack of time concern though. Hopefully Headless UI adds more components over time and the mismatch in breadth will be reduced. I could definitely see a useful library that aimed to do what they're doing but with React ARIA under the hood—I'll hold out hope that it's React ARIA itself one day! |
Beta Was this translation helpful? Give feedback.
-
I think it's difficult to directly compare React Aria and Headless UI because to me it seems that they serve slightly different purposes (I personally use both). I would say that the purpose of React Aria—and @devongovett can correct me if I'm wrong—is to effectively create your own flavor of a Headless UI, either for your own internal design system or for an open source project built on top of it. The components that you create with React Aria can have any API you want. So instead of building a one-off components with React Aria like in your example, you can create a general purpose one with a reusable API. That means you could technically take your Headless UI example and change the import from So to me it's more about the level of control you have over the abstraction. Headless UI comes out of the box ready to go, but offers less customization. React Aria requires some assembly, but you can build any API you want. |
Beta Was this translation helpful? Give feedback.
-
🙌 I'm glad this was raised, btw hi @ianstormtaylor, I'm currently building a text editor with Slate, great lib! I've been thinking about this for a long time. In our org, we are using a combination of react-aria and Radix UI, and it's been on my mind to build a bunch of Radix-like primitives on top of react-aria / react-stately. I think that, while nothing beats hooks when it comes to extensibility and customization, they have achieved a really good balance between having a high-level and easy to use abstraction, and making it extensible. I see two interesting patterns for this:
I bet it wouldn't be too hard to build primitives with a similar approach, on top of react-aria and react-stately. One point where Radix and react-aria diverge is that Radix is designed around native HTML components like Anyway, that's just my two cents. If I had more time I'd definitely try to start a project like this! :) PD: besides this, I think the improvements @devongovett mentions are also pretty awesome, and as a user I can't wait for them to become a reality. |
Beta Was this translation helpful? Give feedback.
-
PD: this thread by @diegohaz (reakit creator) seems relevant too: https://twitter.com/diegohaz/status/1229499924062048260 Seems like Reakit is the most similar to what a react-aria based component library would look like. It has low-level hooks and high-level components, and you can use whatever you want depending on your needs. It basically bridges the gap between the two worlds, and it's similar to how I'd envision this kind of lib. Relevant Reakit docs: https://reakit.io/docs/composition/#props-hooks |
Beta Was this translation helpful? Give feedback.
-
@sandren thanks for the response. I totally get that, I definitely appreciate the flexibility of the hooks from React ARIA, and I'm very glad this library exists. I know you could theoretically build all of Headless UI on top of React ARIA—I'm definitely not arguing for eliminating them. My point is more that I'd bet the majority of users of this library don't require the granularity (and added complexity) that hooks provide. Just look at the difference in imports between the two examples: // Headless UI
import { Listbox } from "@headlessui/react";
// React ARIA
import {
useSelect,
HiddenSelect,
mergeProps,
useFocusRing,
useButton,
useListBox,
useOption,
useOverlay,
DismissButton,
FocusScope
} from "react-aria";
import type { AriaListBoxOptions } from "@react-aria/listbox";
import type { AriaSelectProps } from "@react-types/select";
import type { Node } from "@react-types/shared";
import { ListState, Item, useSelectState } from "react-stately"; With the current state of things... if it's ever possible for me to avoid React ARIA's APIs when adding a new component to my UI kit I will do that. And only when a nicer component-based package doesn't exist will I use React ARIA's hooks. So I opened this issue to bring this up, because I think the library would go from useful to amazing if it provided both hooks and components. (Imagine a @DaniGuardiola thank you! That is awesome. Radix UI seems like exactly what I was looking for—similar to Headless UI but with a larger collection of components. The |
Beta Was this translation helpful? Give feedback.
-
Yep, we're using Radix everywhere it fits for its simplicity, and Aria in places where we need more customization or for things that Radix hasn't covered yet, like the awesome press interactions. I think it's possible to have the best of both worlds, although it doesn't come without complications when you have to combine the two 😆 Hit me up when you try and fail to use a react-aria based button as a Radix trigger, I'll help and save you a world of pain :) |
Beta Was this translation helpful? Give feedback.
-
Hey all, thanks for your comments, this is a good discussion. I agree that having both higher level components and lower level hooks would be great. All of the libraries mentioned are good, but I think they are optimized for different use cases. As far as I can tell, all of them could be built on top of React Aria internally if their authors desired. Here are some tradeoffs with various component-based APIs:
These also tend to get complicated very quickly as soon as you want to do something even a little custom. Animated tabs is a good example. In order to do this, you need to take ownership of the state so you have access to the selection beyond just DOM attributes. Here's how Reach UI shows to do it: https://reach.tech/tabs/#animation. And here's an example using React Aria: https://codesandbox.io/s/practical-monad-punzo?file=/src/App.js. Because you already own the state, it's trivial to add a useEffect and an extra DOM element to render the selection. I think it will be hard to design a single component-based API around React Aria that works for a wide number of use cases. That's why I hope the community will begin to explore different APIs for this that are optimized for specific use cases, e.g. |
Beta Was this translation helpful? Give feedback.
-
I briefly prototyped an example of what this could look like using the Select component in your example above, but moving all of the styling to be passed in with an API similar to what Headless UI offers: https://codesandbox.io/s/intelligent-wright-7wi39?file=/src/App.js. It uses functions passed to This type of API does have a couple limitations though. Rather than passing Headless UI and other libraries solve this using either a registration system using context, or by querying the DOM tree. Both of these require all of the items to be rendered to the DOM to work. You can't implement virtualized scrolling because keyboard navigation wouldn't be able to access items that are out of view and not rendered. That's why React Aria queries the JSX tree for Item elements instead, but it can't do this at arbitrary nesting. For this demo, I instead relied on an Anyway, hopefully this example shows that it's possible to build an API like Headless UI on top of React Aria, and what the implementation could look like. Anyone is welcome to use and extend this code. 😉 |
Beta Was this translation helpful? Give feedback.
-
@devongovett thanks for the follow ups! Thing is though, those examples are sort of proving my point. The underlying logic in the It just reinforces my belief that React ARIA is great because it can do all these things, but I personally hope to touch it as little as possible, because its current abstractions just don't match what most people need. I'd feel irresponsible recommending people base a new UI Kit on it because of the complexity it would force on them, and instead I'd recommend it as a way to fill the gaps as a last resort. |
Beta Was this translation helpful? Give feedback.
-
It's not your fault, I feel the same way, and I'd consider myself a fairly advanced JS/TS/React user. It's due to the way React ARIA is designed, as a collection of hooks that have to be connected in just the right way for it to work at all.
I agree that the functionality is great. What the library is capable of is great. But the idea that because it's only ~100 LOC means it's the best abstraction for the job isn't right. Those ~100 LOC are full of very complex and brittle connections that are very easy to hook up incorrectly. And it's very easy to omit things that either (a) break your component noticeably, or (b) break the accessibility of your components without you knowing. (And that's assuming you're able to glue it all together in a workable way at all.) Most people would be much better served by the ever-so-slightly-less-flexible components that Headless UI or Radix UI provide, due to the much simpler usage and better guarantees that your components end up being as accessible as you think.
Yeah, that's what I'm using it for. I'm very comfortable with React context—that has nothing to do with why using React ARIA is complex. I'm not a BigCo though, so I'm using it to make a It's a design/architecture issue. React ARIA gives you the tools to make it technically possible to build all of these nice, accessible UI components. But it doesn't actually make building them simple (or easy). And it doesn't help guarantee that you hook them up in a way that makes them accessible either. |
Beta Was this translation helpful? Give feedback.
-
The complexities are scattered all over the codebase and docs. Lots of little (and some bigger) things that add up to an overall picture of confusion and frustration when trying to use the library. Here are some...
Uncovering this kind of thing yourself requires putting yourself in the shoes of a user, which can be hard. If you compare all of this to the developer experience you get from Headless UI or Radix UI it's not even a competition. They're usually single-import and single documentation page. Their APIs are much closer to how you'd use DOM components so they're easy to make expectations about. They bake their state in with context so you don't have to repeat yourself. And the accessibility is baked in a layer underneath where you're working so you're guaranteed to get it right. And because of all that, as currently architected the majority of use cases are better served by Headless UI or Radix UI. |
Beta Was this translation helpful? Give feedback.
-
Hey all! 👋 I've been thinking about how we can improve the DX of React Aria recently, and wanted to get your feedback. We're working on this on a number of fronts, but one of the major ones is potentially introducing a higher level component-based API built on top of the hooks that will be easier to get started with, but allows you to drop down to lower level APIs when you need to customize. I've been prototyping what this API could look like in this repo: https://github.com/devongovett/react-aria-components (see App.js for an example). Here are the main ideas:
I would really appreciate any feedback at all on the ideas above and the API. It's still very early and a work in progress. The main hurdle is figuring out how to document all of this in addition to our existing hook API in a coherent way that isn't overwhelming. We want to make it easier to get started, while allowing you to go deeper into the internals when you need more control. So, is this something you would use? What do you think of the API? Any ideas on how you'd structure the documentation? Any and all suggestions/feedback welcome! |
Beta Was this translation helpful? Give feedback.
-
It's great to see this discussion happening. Like many developers I'm also stuck between the low-level implementation of Aria and the prescriptive design of Spectrum. My kingdom for something inbetween - fully functional, accessible, and headless components that allow me to pass style and className to implement my own design decisions. |
Beta Was this translation helpful? Give feedback.
-
Is there somewhere I can be looking to gauge a rough timeline for this work? This is maybe a question for @devongovett who might best understand the scope of work remaining on the new component-based API. I am absolutely pumped about the speedy progress made on this initiative so far, having seen several PRs get merged already, just as my org is setting out to leverage |
Beta Was this translation helpful? Give feedback.
-
Hey all, we just shipped the first alpha of React Aria Components! We'd love to hear your feedback. Check out the docs here: https://react-spectrum.adobe.com/react-aria/react-aria-components.html |
Beta Was this translation helpful? Give feedback.
-
Thanks for the super hard work, I know the Spectrum team put a lot of work into this. I will definitely be trying out the individual components! I love how long and detailed the documentation is. I remember from somewhere that you guys were considering doing some advanced context-based, recursing-the-react-tree-yourself kind of stuff. Are there any gotchas there that people have to worry about? Maybe it's just me but I would love a page describing some of the intricate details of the implementation so that for debugging, I can have the right sort of mental model. I think the react-aria documentation has gotten pretty long; a search bar in the sidebar would be really nice. Also, the hooks documentation pages could also be under their own top-level toggle. |
Beta Was this translation helpful? Give feedback.
-
React Aria Components v1.0.0 has now shipped, so I think we can finally close this out! Thanks @ianstormtaylor for raising this originally, and everyone for contributing your thoughts and ideas. ❤️ Check out our new landing page for all the details on the new API: https://react-spectrum.adobe.com/react-aria/index.html |
Beta Was this translation helpful? Give feedback.
-
This library is awesome for the breath it provides for components and interactions and accessibility. Everything is clearly very well considered. And the goal of it being completely customizable is admirable and why I'd use it.
But as a user, the majority of my time using it is spent in very frustrating to debug "glue code" between the many hooks. It may not feel confusing as maintainers because you know the ins and outs of every hook, but let me tell you as a user, this is an extremely confusing library.
A big part of this is because of the decision to only provide hooks as the main API. The existing hooks are great for low-level use cases where some hyper-specific customization is required. But 90% of use cases don't need this.
I think this library would greatly benefit from taking an approach similar to Headless UI, which is based around components. With smart use of internal context to connect things together they are able to eliminate all of the tedious glue code, allowing you to focus purely on the components, state, and events. It's wonderfully simple to reason about. (And the existing hooks API could still be available for the 10% of cases that need extreme customization.)
For context, take a look at how implementing a Listbox differs between React ARIA's current API, and how HeadlessUI does it...
React ARIA
HeadlessUI
The HeadlessUI code is much easier to reason about because all of the glue code is handled under the covers by the library using context. Whereas the React ARIA code is full of hooks and refs and state that needs to be connected together just right to get things to work.
Edit: Unfortunately I wasn't super clear, but I'm advocating for both hooks and components APIs. Obviously the hooks are low-level and useful for edge cases. But I think adding components alongside them would be nicer to use for 90% of cases.
Beta Was this translation helpful? Give feedback.
All reactions