Placemark has plenty of moderately complex UI patterns that benefit from React component primitives. We’ve got dialog boxes, tooltips, popup menus, things you can drag, search boxes with autocomplete.
Building these from scratch is usually a bad idea, because you’ll implement the parts you notice, like the visual effects and experience of clicking around, but miss all of the things that are important for other modes of usage, like how the components interact with tab focus, keyboard shortcuts, and ARIA roles. You’ll have to solve hard problems from scratch, like how to position a popover in a way that works with z-indexes of other parts of the page.
When I was working with Rails, the default answer was Web Components. Web Components are… controversial. Folks like Rich Harris have described their issues well. For that project, they worked great, because we had limited expectations, stuck religiously to GitHub’s battle-tested components, and did a lot of server rendering. So the autocomplete results in an autocomplete UI were rendered by the server itself, rather than transferred as JSON.
Placemark is a React app, and does most of its work in the client. Web Components could fit in a few places, but there are many more complex interactive situations you trigger, many more dependencies between parts of the UI. It is, as I’ve roughly described before, a good fit for React.
So, what do I use to get all of these little fiddly parts going?
The broad categories for React components in my head are something like this:
For example, react-autosuggest was the best solution for auto-suggesting search in React for years. Unfortunately, a lot of these projects are stagnating because of the well-documented problems with open source being profitable, fun, sustainable, or generally a good idea as a hobby. Also, you have to deal with different approaches for things like TypeScript types, how these components do styling, and different styles of documentation. Generally: it’s extremely impressive what folks are able to build, but it’s a nicer experience and a better long-term bet to use a unified ecosystem of components.
Like Material-UI, Ant Design, and Chakra UI. These always surprise me: the Material-UI homepage looks like what it looks like, and the library has 68,740 GitHub stars at this writing. Ant design has 72,180 stars, and is… a mixed bag. Chakra is probably the best effort out of these. I heavily suspect that this is a culture gap: Ant is based out of the mega-corp Ant Group and has lots of Chinese documentation.
Anyway: I don’t want components that are that high-level: these components will encapsulate complex DOM structures that you don’t really know about, adding hidden buttons and divs to support this or that design element. I want to use abstractions for efficiency, not ignorance. Also, the default styles of these libraries always diverges from what a complex application needs. Especially Material Design, a design system that thrives in low-information-density webpage displays and struggles in high-information-density applications.
This is where I land, and there are basically three big ones:
I’ve used all of these in some portions. Radix & Reach are similar in terms of abstraction: they both give you component hierarchies, polished interactivity and accessibility by default, and the ability to style things in a variety of ways.
react-aria is lower-level: instead of components, it gives you hooks. So every last element on the page is something that you’re acutely aware of, and you connect the pieces together. You’re building a design system from the ground up. The react-aria examples tend to be longer and a bit more complicated than the examples for Reach or Radix. More decisions are deferred to your application. All of the document structure is yours to decide.
These hooks are very neatly composed: higher-level hooks like useMenu are built on low-level hooks like useFocus. I find myself using a lot of both, and composing new hooks to support new functionality. react-aria’s documentation is unique in explaining not just the obvious features of each hook, but all of the additional input modes, and often why the hook is useful as an abstraction or replacement for a native HTML element.
Placemark is currently using react-aria for practically everything that requires a component library. It certainly has its drawbacks. I’ve noticed things like:
as anytype system cop-outs as a result. The library does use TypeScript internally, and exposes quality types, it’s just an issue with TypeScript examples, which might get added.
But none of these qualms outweigh react-aria’s success. It has a big idea - that with React hooks, you can make a successful library that adds functionality and accessibility onto any DOM structure that you provide. That idea works really well: it simplifies the virtual DOM, it sidesteps the wild-west chaos of CSS-in-JS libraries. It stems the props bloat that afflicts super-flexible component libraries.
Also, react-aria is very well-documented, and its contributor base is only mildly top-heavy: Adobe appears to be devoting several people’s work time to the library, over the course of several years. The future is pretty bright for this library.
I think that complicated UIs are going to be complicated to implement, no matter which tools you use. My choice of react-aria for a lot of these pieces isn’t based on it being the easiest option, but the idea that there’s virtually no limit to what you can do with it, and the nonfunctional aspects - its maintenance and documentation - are so strong.
This choice is, then, a result of its inputs, the requirements and future plans of Placemark. If I was building something simpler and on a tight timeline, I’d strongly consider something like Chakra. But so far, for this purpose, this is working well - I’m spending more time on features than on reimplementing a dialog box for the tenth time.