Hacker News new | past | comments | ask | show | jobs | submit login
React Aria Components (adobe.com)
135 points by itsuka on May 7, 2023 | hide | past | favorite | 58 comments



Oho! So excited about this. I’ve used a hybrid of Radix UI and React Aria on previous projects. Radix had a much nicer DX out of the box with pre-wired components, but my team was always apprehensive about the long-term support for Radix post-acquisition. Really excited to see React Aria adopting a similar component-based interface.

Couple questions/feedbacks if you’re still in the comments, Devon!

1. Radix implements an asChild prop to help merge the library components down into other components that might already have their own styling or behaviors. Did y’all consider supporting this sort of polymorphic approach? Or is there another way to merge components? The main spots where I’ve used it are buttons and dropdown triggers, so it might not be broadly applicable.

2. Is it easier to extend from the React Aria types with this interface now? My previous implementations with React Aria - I’ve always been a little confused about which inputs to the hooks are useful to include in the component’s props interface and how to correctly annotate them, whether that’s recreating the types or extending from the numerous options within the react-aria package. Radix at least has some suggestions for conventions, and I feel a bit more confident following them.

3. More on the feedback side - React Aria has typically implemented similar-but-different props for the common HTML form control attributes like “disabled” and “onChange” as “isDisabled” and “onValueChange” - I really appreciate these at one level for the internal consistency throughout the library, but I’ve found these choices make it a bit more annoying to integrate React Aria with other third-party libraries meant to “just work” with the regular attributes. E.g. spreading Formik form props for a field into a form control, but having to re-map the onChange prop to onValueChange. Definitely trade offs in either direction, but I’m curious how y’all made the choice and if there are some remediations y’all have found along the way?

Thanks so much for your (and the team’s) work on this! And on Parcel and the million other projects you’re building.


Thanks for the feedback!

1. We decided to start simple for the first release but it’s definitely possible to add something like asChild if the need arises. There are some tradeoffs to this though, eg it’s easier to mess up the DOM structure required for accessibility. There are some other approaches we’ll be documenting for this as well.

2. TS should be easier now. All the docs examples are now written in TS so you can see where the interfaces are imported from.

3. Yeah prop naming is a trade off. When we started, we decided to make all of the components follow a consistent naming convention. The DOM is quite limited in its functionality (eg you can only submit strings and not more complex objects) and we knew we’d need quite a bit more capability so we decided not to follow it in some areas. But we know integrating with form libraries is a pain point and we might have some documentation or helpers for that in the future.


1. Understood! Yes, I agree about messing up the DOM, and I'm sure you've seen some egregious cases haha. asChild definitely enables some less deliberate choices when composing bits and pieces together and assumes some good habits around event handlers - probably not an approach that scales well for growing teams, but definitely convenient for hacking on things. Can I bum you for a link or some search terms for the "other approaches"? I'm curious about them.

2. You're spoiling us. Thank you. Re-visiting the hooks docs, I think I was just meant to use the `Aria...Props` interfaces the whole time haha. I have a feeling I overcomplicated some things in that codebase.

3. "you can only submit strings and not more complex objects" - I think this is the crux of the tradeoff. Frankly, the only thing I use Formik for when I bring it in is binding Zod schemas to form state to get validation messages in a pinch. If React Aria had a first-class schema-driven Form component or similar, I'd be a satisfied "customer".

Had another random question overnight -

4. Have y'all considered implementing anything in the realm of the "CSS Spatial Navigation" proposal? The conversation around it looks like it has gone quiet in the past few years, but I think there's definitely an opportunity for non-grid-y 2D navigation in web UIs.


Libraries like this are what keep me using React. They’re (imo) table stakes for building any serious project. I really want to use Svelte and Solid more, but the choices for building complex widgets like dropdowns are either “forget about accessibility” or “sink a bunch of time into implementing it yourself”. And let’s face it — most self-implemented widgets are accessibility nightmares.

What I really wish is that there were something like this built on web components, so I could use it with any framework.


React Aria is particularly unique because of the date picker. There's extremely few components that let you do proper date picking, nevermind date range picking, in a relatively accessible way. Even input type=date in the browser is an accessibility trainwreck.

If you need strict accessibility and date picking, you're either making people enter dates in a masked input field or you're using react-aria. I'm sure there's a few other options out there, but you have to dig long and deep for them.


I hate to sound like I work for Tailwind again lol, but my current employer probably has one of the strictest set of accessibility standards when writing new code for them.

I found that the React samples on Tailwind UI, and their base component library, cover pretty much angle of accessibility.

I was working on a legacy angular upgrade to React upgrade for my employer and they were concerned I would downgrade their current compliance levels moving to Tailwinds components. I actually ended up drastically improving their compliance levels by following Tailwinds lead, and nothing was lost. For example, moving from font awesome to hero icons allowed for the ability to inject a screen reader only tag into the icon, allowing the screen reader to output an icons function.


> Libraries like this are what keep me using React. They’re (imo) table stakes for building any serious project. I really want to use Svelte and Solid more, but

And not just those but bigger alternatives like Vue. I've been looking for soemthing as good as Radix UI or React Aria Components or Atlas UI to use with Vue but it's just not there.

Much as I like Vue 3 the ecosystem is winning with React


You might be interested in the library ZagJS, which has components for React, Vue, and Solid. I came across them in my search for a component library for my team. The docs are pretty impressive. React Aria edged it out simply due to the kind of components it had available.

edit: grammar


This actually looks fantastic! I had hoped when I said “this doesn’t exist for other frameworks” that Cunningham’s Law would come into effect :) It looks like it’s made by the same person who created Chakra UI, too!


I am the opposite. Developing these components is like the most trivial task when starting a new project. Then again, I am not a React guy and usually frown at these prebuilt libraries as they tend to be opinionated and unpleasant to change for the use case at hand.

I don't know. Maybe I just don't like "ecosystems"


They're definitely not trivial. Do you build them well? I don't mean this as an insult to your work; I just know that these components have a million little details and edge cases that makes them not trivial if you care about getting them right.

Let's take a modal, for example. The easy part is clicking a button and having something show up on top of everything else. But do you…

- make sure screen readers announce it correctly when it's opened?

- make sure it has the correct ARIA attributes?

- trap focus within the modal so that you can't tab to an element in the page behind it?

- allow it to be closed with escape?

- return focus to the correct element when it closes?

These aren't arbitrary requirements I'm making up. The W3C has detailed documentation [1] about how people should be able to interact with these components. It's not something I want to spend my time doing, and frankly when it comes to things like ARIA attributes and screen readers it's not something I'm confident I'll do well. That's why libraries like Radix and React Aria are so valuable. You get rich, accessible components basically for free.

[1] https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/


Boss: "So, when will the MVP of the screen be ready?" Dev: "Well, we need to hire a few more people and the build the date picker and select box, and then we can actually work on the stuff you asked. So about 2-3 months. Then we can start tackling the i18n and a11y bugs. EZPZ".

UI libs definitely have their drawbacks, but using headless component libraries like Radix or react-aria with your styling solution of choice gets rid of 90% of the problems they generally have.


You write your own component library when you start a new project?


It's better than dealing with vuetify and such. Writing this stuff is the easiest thing in a new project.

Am I out of touch? What do you do when you have tons of custom functionality and styles? I spent about an year in a huge react only shop and I swear these guys were spending most of their time debugging other people's crappy components. Most of them had your reaction when we talked


It’s a hundred times easier to build <shitty button the designers want with 6 states> from scratch than style an existing component from a component library. I would only use a component library for the things that are hard, like dialogs and multi select weird option lists etc.


Buttons, sure, you can mostly just style the built-in <button> tag. Libraries like this are useful for things that don't come included and are tricky to get right — custom selects, modals, dropdowns, toasts, etc.


What about accessibility? That's the biggest thing in my mind when I choose to use someone else's component library. Things like combo boxes, date pickers, popovers are non trivial in terms of placement and accessibility.


I understand that this release is marked alpha, nonetheless it's pretty funny that opening the dropdown in the first example snippet on the page disables scrolling for the whole page. While it isn't trivial, custom dropdowns that work regardless of container overflow are a solved problem, just pick one of the common solutions and use it.


This was an intentional choice. Repositioning when an outer element scrolls is pretty janky in some cases because scroll events don’t fire at 60fps. Also it’s not possible at all in other cases, like if the trigger goes completely out of view. We used to close the popover in this case but this caused usability problems. The new behavior of preventing scroll actually matches native platforms like macOS and helps with these issues. I get that it’s a little opinionated but it was thoroughly considered, not just done out of laziness. More details in this answer: https://github.com/adobe/react-spectrum/discussions/3802#dis...


I reckon it’s a mistake. Hiding scrollbars dynamically is unpleasant, and the degenerate cases like `position: fixed` seen on this particular page make it very unpleasant.

Embrace what the web exposes. Allowing document scrolling underneath is distinctly the lesser of two evils. With a bit of effort, you can block scrolling underneath in almost all cases, and I reckon that’s good enough and much better than what there is currently. (Also try asking browser makers, “y’know this ‘top layer’ thing you made for <dialog> modal presentation and fullscreen? Wouldn’t it be nice if we could prevent scrolling underneath a ::backdrop?”)


Facts. You have to forgo native scrolling and basically re-implement all desired scroll behavior in JS in order to get rid of fixed position scroll jank.

I've been interested in re-implementing smooth scrolling in JS to mimic native but.. Even if you hit solid native refresh at all time, I feel you'd have to switch your entire site to JS controlled scrolling for consistency and even then the scroll feel might not match native scroll on other pages per system settings.

It's a bit of a rabbit hole.


Never override scrolling in this way. Scrolljacking is always bad, because the web doesn’t expose the primitives required to make it anything else. You might be able to get it to match the platform you’re on exactly, but it will still be a trainwreck for many other users.


Okay


Nice work! Interaction design has been often reduced to a shouting match, and it’s great to see a more considered approach being taken.


Couldn't it be anchored to the page? Instead of anchored to the screen and require repositioning?

Is there a limitation in CSS preventing it?


The problem is the drop-down should never be cropped/hidden by the boundary of a parent container.

This runs counter to the behavior of it being anchored to a parent container.


Are you saying that you can only anchor an element to one of its ancestors? Can CSS really not define positional relationships between siblings (I'm used to Apple's Auto Layout)? If so, couldn't you add an additional outer container that doesn't crop its contents to its bounds and anchor the dropdown to that?


The problem is when your dropdown component (D) is in a scrolling container (2) in the page (1).

    -1-----------
    |           |
    |  -2---    |
    |  | D |    | 
    |  -----    |
    |           |
    -------------
If you attach the dropdown options to the inner container (2), they are cropped.

If you attach the dropdown options to the outer container (1), they don't scroll with the rest of their component (D).

----

Non-native dropdowns in general are handicapped.

For Windows/MacOS/Linux, native dropdowns can exceed the bounds of even the OS window. There is no CSS equivalent of this behavior.


If you have a trigger inside a nested scrollable region, and the popover pops out of this region, then if the trigger is scrolled out of view the popover cannot be anchored to it anymore.


For more information on the actual issue this comment mentions:

The page applies `overflow: hidden` to the root element, which isn’t a very good solution to the “you shouldn’t be able to scroll the document while a dropdown is open” problem. It tries to counteract the scrollbar shift problem this causes by applying corresponding padding to the root element (which is still yuck, because the scrollbar still visually disappears, which it shouldn’t), but the page uses `position: fixed` for the header and right ToC sidebar, which bypasses the document padding. End result, opening a dropdown shifts parts of the document, which is bad.

I’m not sure if there’s a proper solution that makes it behave how a <select> does on desktop platforms; historically there wasn’t, and every historical workaround causes worse problems than it solves, in my opinion. Also in my opinion, the problem was never particularly important; the only part that was even vaguely notable was scroll chaining, and you can now just specify `overscroll-behavior: contain` on the dropdown popup to categorically fix that. (Browser support: 7½ months in Safari, 5–6 years elsewhere.) Very recently, browsers have been getting better support for modal concepts, driven by the <dialog> element and the corresponding concept of a top layer, as the spec calls it. I haven’t played with it, but I suspect that this might finally be the actual solution to the problem of stopping scrolling underneath.


The most common solution used today is Popper, which essentially takes the relevant element out of the DOM and makes it a fixed element with transforms on body (or any element of your choice) instead. It's used by many large projects such as react-select etc. This is an approach that wouldn't have worked well in the days of old due to delays in updating the UI, but modern browsers are very capable with it and there are no discernible issues with alignment. I agree however that native elements are way better wherever that's a valid option since they also have corresponding native elements on mobile devices etc.


I think you might have misunderstood the situation.

Popper supports putting popper elements somewhere else in the DOM, but that’s not their general recommendation (https://popper.js.org/docs/v2/performance/#attaching-element...).

React Aria puts its popups in such a separate place in the DOM like you’re saying.

But the part I and the parent were remarking upon is what you do about scrolling.

Popper’s (default) answer is “recalculate the preferred position [and size, I think?] on every scroll event”. This is perfectly fine, and sometimes preferable.

React Aria’s answer is “position once and then prevent scrolling”. This matches all platforms’ conventions for these sorts of elements, it’s just that the way they’ve implemented it has problems, problems that may be unavoidable at present.


Is it possible the doc page is just not laid out well and that's causing weird interactions with how the component tries to stop scrolling? I imagine it also tries to stop in the nearest scroll port, which just happens to be the root here.

In any case I'm sure they will fix it if you open an issue against it..


Isn't the proper solution to just use the HTML <select> element, instead of one that recreates the visual, recreates the behavior with JavaScript, and recreates the semantics with ARIA?


<select> is terribly limiting. Options can be a single line of plain text, with some platforms letting you control colour and font, and you get only very basic selection by typing, on desktop platforms only in general.

The “Favorite Animal” dropdown shown in the linked page can be done with <select> with no loss, and therefore I’d say probably should be. But you can often do much better by skipping past <select>:

• Use more than just plain text in each option (very simple example: https://react-spectrum.adobe.com/react-aria/Select.html#comp... also think of things like adding icons)

• Support typing on all platforms (and no, native combo boxes à la <input list="…"> + <datalist> aren’t far off completely useless in practice).

• Support typing with more complex matching, e.g. in a country selector supporting US/USA/United States, NL/Netherlands/The Netherlands/Holland, &c.


Funny enough, all of those examples are broken on Firefox for Android; they show on tap, then immediately hide.


Also it doesn't appear to work at all on android Firefox - tapping the dropdown just shows all entries and then they disappear before you can select one!


Interesting. I’ve loved using react-aria on past projects even with the amount of glue code you needed to write sometimes. Its not even that it was so much typing, but devs instinctively tried to abstract away reused code (“DRY!”) but would then inevitably run into issues and risk creating monstrosities… I’m thinking specifically of anything that used popovers…

I do like the idea that you can use these components as a starting point and drop down to first principles with react-aria hooks if it suits the use case.


Having managed and led design systems teams, I really don't understand why more of them don't build off of kits like these. So much money is wasted in teams saying they need to build components from scratch.


Amazing. Been building a custom system on Aria while looking at the spectrum code for learning, familiarity with their architecture, and to avoid their style system as we are using tailwind..

I'm excited to try these out and possibly switch over and fill in the gaps. Lots of headless systems are locked into a particular styling system but this looks super flexible and will work perfect with Tailwind. Love the callback for classname that supplies states.

Hope they keep the slot props concept from spectrum.

Edit: Slot props are in! Can't explain how much I like the Spectrum projects.


What are slot props?


I'm basically doing the same thing as you but I'm also looking through Mantine's source code to complement it.

I haven't really grasped what slot are used for tho, can you share a tldr?


It's a set of conventions and utils allowing a parent component to mix properties into slot-aware children so they can be styled and configured to fit within the parent context.

https://react-spectrum.adobe.com/react-spectrum/layout.html#...

Too bad React doesn't have Vue slots built-in, but this is nice for streamlining component APIs. Works well with Tailwind classes.


See also Radix UI https://www.radix-ui.com for a similar library of unstyled components that focus on handling the interactions for you.


The `Link` component is ridiculous: “Links can be created in HTML with the <a> element with an href attribute. However, if the link does not have an href, and is handled client side with JavaScript instead, it will not be exposed to assistive technology properly”…

Sure, sure, but if your link doesn’t have an href, it’s because you don’t want a link, you want an unstyled button!


We’ve been looking to port to this. We used Reach UI but that seems to be a dying project.

It’s really nice to be able to get aria gains without giving up creative control of the visuals.


I used this on a project recently and I regret it honestly. It just made unnecessarily complicated. All the docs depend on the sibling library react-stately also, so you'll definitely want that also if you're using react-aria.

I wonder if anyone here has used react-aria and also headless-ui and have any notes about their comparison.


Will this be adding a Box component hopefully?


Is this for AEM frontends?


It’s suitable for any React codebase, and it’s based on an existing library of React hooks for managing accessibility, which was also created by them.


I see, thanks.


So, for someone who couldn’t care less about UI but focuses on UX all the time; does this help me? It looks like the same awful as everything else frontend to me. Why is it interesting? Honest question?


This blog post explains why developing accessible components (that work well across devices) is not an easy task: https://react-spectrum.adobe.com/blog/building-a-button-part...

There are so many UX pitfalls that I prefer to delegate the task of fixing them to a library, while still retaining control over how I want to style them.


I feel like the first paragraph - What is React Aria Components? - explains it pretty well.

It's a set of unstyled components components for common UI patterns, with accessibility, internationalization, interactions, and behavior built in, allowing you to focus on your unique design and styling rather than re-building these challenging aspects.

If you don't like doing UI/styling, this probably isn't for you as these components require you to style them yourself.


I have to ask: when posting a question like this, what kind of response are you hoping to get?


Well, the answers I got actually helped me, so yes, that was what I was expecting. I could’ve/should’ve asked it in a less angry way, I agree. It was early, but that is no excuse.


We've all been there :D


Especially if your most interested in UX, it's be worth going down the rabbit hole of web accessibility. Building sis that work as expected for visitors with visual and physical impairments isn't particularly ready and often skipped entirely, especially in react.

I've never used this particular library, but if it's does what it says on the box it should make it easier to have accessible components out of the box so the developer is only focused on making it look good.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: