Video Summary and Transcription
Headless Components are efficient for app development, but there's a lot of work involved, especially for menus. The customizability wall is a problem with component libraries, but it can be solved through reverse engineering and design. Headless Components offer no markup or basic markup to be overwritten, providing flexibility in code and design quality. Radix and React ARIA are recommended stylus component libraries with different APIs. Kodaks' experience with headless components highlights the ability to mix and match easily and the potential for market gaps in the headless space. Radix is a popular choice for headless components due to its well-documented and user-friendly API. Headless components aid in testing, distribution of design systems, and accessibility. MUI is a self-consistent and rich library, while Radix focuses on accessibility and default accessibility. Kodaks integrates well with headless libraries and welcomes feedback through Discord.
1. Introduction to Headless Components
I'm Omri, the CTO for Kodaks, a Wix company. Today, I'll be discussing Headless Components, their importance, how to utilize them effectively, and popular options. We'll also share our experience at Kodaks. Components are efficient for app development, but there's a lot of work involved, especially for menus. Many of us use component libraries like Material UI and Ant Design to simplify the process.
I'm Omri, I'm the CTO for Kodaks. Kodaks is a Wix company building awesome tools for developers, and I'm going to be talking about Headless Components. What they are. Why you should care. How to best utilize them. We are going to go over some popular options and see it by example and of course I'm going to share our experience at Kodaks.
The reason I chose this topic is that I see a great opportunity here to exploit a gap in the market in a way that will help all of us. Shameless plug, Kodaks is out, it's free, it's an open beta since Christmas. You can follow along in the link in GitHub. If you're using desktop, it's an IDE, it's not really for phones. The way it works is you edit react components visually and in isolation and it's a great, very effective way to edit your components.
Components are great, that's why we all use them. But we get paid to build apps, right? Components are just a very efficient way of doing that. By the way, this is the only reference to AI, so my gift to you is like ten minutes without AI. Back to our app, we want to build something like a Google docs clone and there is a ton of work. In that work, there is this menu thing. It's a pretty basic menu. And if we're feeling very naive, we might say something like, how hard can it be, right? It's the HTML and CSS is trivial. It has open and closed states, a bunch of items. Items go click, the end. Except for some secondary behaviors like little things, like, you know, z-indexing, accessibility, resizing, pinching, scrolling, focus, keyboard navigation. Well, it's actually a lot of work. And this is not even stuff that's on the spec, right? This is just stuff we need to do to ship quality products. And I guess that's the reason many of us use component libraries. Either you build your own over the years, which you roll over from one project to the next. Or you use an open source or commercially available ones. We have Material UI, Ant Design. It's a big spectrum. Some of them are really good. They all have the same premise.
2. The Customizability Wall and Solving Problems
Learners talk and you'll be able to develop good looking UI very quickly. But there is this problem with all of them, right? I call it the customizability wall. And here is the anatomy of the customizability wall. You promise your product manager a bespoke business logic accessible through a beautiful, unique design. And your design and your component library of choice are having a fight. So just by show of hands, we're here smashed into this customizability wall. And already we see that there is a problem. The data structure doesn't take an icon for the group, and we can see that indeed nothing changed. But for some reason, we can't add it to the group, and we're kind of stuck. Like, can we solve this problem? Of course, we can. We can reverse engineer and design.
Learners talk and you'll be able to develop good looking UI very quickly. And the good ones really do deliver on that performance. But there is this problem with all of them, right? I call it the customizability wall. And you smash into it pretty late in the project when you find that your table and your multi-select don't feel or don't look the same.
And here is the anatomy of the customizability wall. You promise your product manager a bespoke business logic accessible through a beautiful, unique design. And your design and your component library of choice are having a fight. And if you work like me, this is especially frustrating because you see it very late in the game because I like to build fast UI to kind of get the point of the business logic across to the client, do some iterations and then do the fit or finish only on the features that are actually going to be shipped.
So just by show of hands, we're here smashed into this customizability wall. All right. So for the lucky few that didn't raise your hand, let me show you an example. And I'm going to be using Codex to show the examples. So this is our IDE and this is the expected behavior. This menu is from AntDesign. It's a really good component library. It's highly customizable. And what we can see here is that the menu has a submenu and the items can be grouped. And my design calls for an icon in group A. We're going to just recycle this icon here. So let me grab it. And this is the data that generates the menu. I'm just going to copy and paste it. Beautiful. And already we see that there is a problem. The data structure doesn't take an icon for the group, and we can see that indeed nothing changed. Just to get the point across, I can add it to the actual subitems. Right? You can see the icon. But for some reason, we can't add it to the group, and we're kind of stuck. Like, can we solve this problem? Of course, we can. We can reverse engineer and design.
3. The Benefits of Headless Components
We can extend the menu, but it's a bit of a hack. The dilemma between code quality, design quality, budget, and deadline. The problem starts with components. Headless components offer no markup or basic markup to be overwritten. They provide the main and secondary behaviors in component-based and hook-based APIs.
We can extend the menu. We can parse a CSS rule that targets this individual label and fixes our issue. But it's a bit of a hack. All those solutions are going to look different than the way we customize the rest of the menu. And will they survive the next upgrade of our dependencies? Maybe. Probably.
So this leads us to like a dilemma between our code quality and the quality of our design and our budget or our deadline. By the way, this isn't me disrespecting Ant Design. I think they're great. And I think they're really customizable. The problem starts with components. Right? So I'm going to create like a my-text, my-text component, and let's just look at it in VS Code so I can have a nice big font. All right. If I don't define a prop called class name and I don't spread it on the right HTML element, then the classes are not customizable from the outside. This is a problem or a feature of React components. So any author of a component library that comes with like a default view has to opt into everything that can be customized. The only way to make everything customizable is to not have a default view. And this is exactly what headless components are. You get either no markup or a very basic markup that is designed to be overwritten. And you get no styling whatsoever.
This is a pretty interesting offer. Like, we still need to generate all of the HTML, all of the CSS, but you know what? That's the easy part. This is defining your view in the language that you know. And what you get for free or by using a library is the main behavior and all of those secondary behaviors that would have taken you weeks to write and debug. And they come in two flavors. The APIs of headless components come in two flavors. One of them is component-based. So you get an abstract component which takes your view or snips of your view as children. Or you have the hook-based API which just gives you a hook and you need to construct a component around it. And it gives you all of the functionality you need.
4. Examples of Stylus Component Libraries and APIs
Radix is a great stylus component library with default HTML and a lot of primitives. It offers a JSX-based API where everything is defined explicitly in the code. You can override the default HTML using a child property. If you need more than primitives, check out React ARIA, a hook-based API and part of the Adobe Spectrum offering.
This is a bit abstract. Let's look at examples. So, Radix. Radix is a great stylus component library and styles which means it does have default HTML. It's very basic and you can replace it. It comes with a lot of very good primitives. It looks something like that.
So this is what I meant... Sorry. This is what I meant with the JSX based API. We have this component here and we can see that we have those components that make it up. We have a popover.root, a trigger. We have the portal that has the content. But everything you see on the screen, all of the view, is defined explicitly in the code. I mentioned that it does come with a default HTML. If you want to override it, this is a nifty little feature. You add the property as child and that will take your HTML snippet, and we'll use it as is. It's not going to wrap it with whatever default view it comes with. And it will add the different props like ARIA and event handlers, etc. Really nice library. I really like their API. Their docs are incredible.
However, it is limited to primitives. So you're not going to find a date picker or calendar. If you need those, you might want to look at React ARIA. React ARIA is a part of the Adobe Spectrum offering. It's like a full-blown design system. But this is one of the under-the-hood libraries that is open source. And it's also an example of our second type of API. This is hook-based API.
5. Exploring the 'Use search field' Hook
The hook 'Use search field' is a nice and elegant API that returns props to be spread onto the chosen HTML. However, it relies heavily on refs and uses React Stately for state management.
It looks something like this. So the behavior is the user typed their search. And then you have this little button here that clears the search field. Cool. Let's see how this component is defined in code. All right. So we have this hook. Use search field. Takes a bunch of parameters and returns these props that are meant to be spread onto the HTML you choose. I find this API really nice. Like very elegant. It does come with a downside sort of. Use a lot of refs. Very liberal with the refs. And it kind of piggy backs on a state management library called React Stately. It's also part of Spectrum. So other than that, I would say it's a very interesting library. Lots of interesting components.
6. Exploring React Table and Customization
React Table is a big piece of software with a lot of functionality, which means a more involved API. However, using the hook to get the table simplifies the process. While it may require some initial learning, transferring skills to this project is easier than with a proprietary alternative. Customizing the view of a complex table without parameterizing everything can save time and effort. Although it may seem like more work, it reduces the chances of a rewrite and mitigates the risk of going over budget and sacrificing sleep.
So far, I kind of focused on primitives. Let's go to the far extreme of the complexity spectrum and look at React Table. One year ago, the author of React Table, gave a great talk on this very stage explaining why he went the headless route with React Table. One of the points he made, by the way, great talk, highly recommend checking it out, and one of the points he made is about simplicity of APIs.
So, React Table is a big piece of software. It does a lot. It's quite advanced. It can react to changes in the data, and a lot of functionality. A lot of functionality means a more involved API, right? We do more, we need more to describe it. So, let's see how the API looks like. I'm going to open this in VS Code again. So, we have something called a table. It has getters and setters, and it gives you a list that you map on to your view. And you might think, like, this is involved, right? This is a lot of mapping, this is a lot of stuff I need to know. But actually, all you need to do is use the hook to get the table. Everybody knows how to use getters and setters, everybody knows how to use a map, right? And everybody knows what this piece of code does. This is just an HTML snippet.
So, after an initial getting used to period, your skills can be transferred from whatever you're used to onto this project much more easily than a proprietary alternative, right? To think about how much you can customize in the view of such a complex table. If you had to parameterize everything, just a sheer number of properties you need to dock is going to completely outshine the interesting functionality parts. So, at this point, you might think, wait, so it's just more work? And I would say yes. I mean, if you're building a POC, yeah, it's going to be more work to write some HTML and for real project with a unique design, you will probably have to customize all of the defaults, right? You don't want your application to look like any other application that was developed using this components library. So you're going to have to rewrite it. You're going to use a proprietary way of describing the changes. It's not going to be less code, it's not going to be less work. And I keep banging on the proprietary side of things. But when you onboard new team members, it makes a big difference if they need to learn everything from scratch or they can transfer their skills. I would also argue that it is less work because it reduces the chances of a rewrite. If you hit the customizability wall later in the game, as you do, you are in the money time of the project, you're probably over the budget and you want to get it done. And whatever solution to this problem you come up with, it's going to be at the expense of your sleeping time. And as a person who values sleep, I find mitigating that risk very valuable.
7. Experiences with Headless Components
Our experiences with headless components at Codex taught us that you can mix and match easily. Headless components help maintain separation of concerns and create a nice boundary. We wrote our own headless library, but there are great libraries like Radix available. The headless space lacks daily drivers like advanced forms and galleries, creating a market gap that can launch careers.
So our experiences with headless components at Codex, just some lessons learned. We found that you can mix and match pretty easily. You don't have to commit. You can start with what makes sense, and we still have both flavors, like headless and I guess headful components living together, playing nice. We also found that headless components help us keep the separation of concerns for longer, right? Because the behavior and the view are already separated, to mix in the business logic is like no developer wants to be throwing the first stone. You don't want to dirty something that looks clean, and psychologically, it creates a nice boundary.
So, this is a painful one. We wrote our own headless library, okay? By the time it was mature enough to open source, Radix was already out, and we said like, ah, this is — it's not what the world needs right now. But our tech debt is your win, right, because it is out there. These are great libraries. You can use them. You should check them out. Use them in your projects.
Back to the opportunity. Right. So, I showed you some great primitives and some niche, complex pieces of kit. But what I think is missing in the headless space is the daily drivers. Like advanced forms, galleries, car sales, multi-select. All that stuff that we kind of need in almost every app are in short supplies in the headless space. And I think that this is a market gap that can launch anyone's career. And I hope one of you will be on this stage next year and tell us how filling in this gap took your career to the next level. And with that, thank you very much. I'll be answering questions right there.
8. Choosing Radix for Headless Components
I've been building with Radix for the last year and a half and it's made my life so much better. I would go with Radix because of their well-put-together documentation, user-friendly API, and active development. Radix is my personal choice.
Great talk and really important topic. I've been building with Radix for the last year and a half and it's made my life so much better. So, if you hadn't built your own, what would you go with right now? What would be your personal choice? I would go with Radix just because I really liked their... Most of the documentation is really put together nicely and secondly, I like the API and I like not having to use this many refs. There are other alternatives that are similar, but Radix has been the most active, they've been the most responsive and it has the biggest components count in the library. Yes, I would second that personally. Radix is dope.
Unit Testing and Styling with Headless Components
Headless components make it easier to keep the separation of concerns, which aids in testing. They can be used to distribute the same design system to teams working with different frontend frameworks. Styling headless components can be a challenge, but different libraries offer better solutions. Radix has good accessibility and helps developers make things accessible by default.
All right. So, we have some questions here. What about unit testing? Is it easier with headless components? I wouldn't say it's easier. I would say that it makes it easier to keep the separation of concerns, which makes any testing easier. So, if you're doing a good job keeping it separate, it's another tool in your tool chain.
Could you use headless components to distribute the same design system to teams working with different frontend frameworks? It's a great question. I would say that it depends on which component library you use. Because it's headless, you can actually take something like Material Ui, and put it as a snippet of some part of a component. And in React Table, they actually have examples of using Material Ui and a bunch of other frontend libraries as part of a bigger table.
One thing that I struggled a little bit personally when we were using radix is that we were, at the time, using a CSS and JS type of solution. It was not always easy to style the headless components this way. Are there styling solutions that are maybe better suited for headless patterns than others? I would say not to CSS and JS in general. But I would say that all of these libraries lend themselves to different styling solutions better than the alternative. And I did not run into that specific problem. I don't know when you had it. But I think radix and a lot of these libraries made a conscious effort to separate themselves from your styling solution. Eventually we migrated to Tailwind and we solved the problem that way. You always have to chase the trend. Tailwind is the new hotness. We had to rewrite our entire app. You have to pay your dues to Tailwind.
Here's a really important question and one thing that has been the biggest benefit for me using headless UI components is the accessibility story. So does radix have as good accessibility as Spectrum Aria or are there different strengths and weaknesses? From what I've seen, it's much better than I would have done manually, and it's just good. To tell you that we have one of our developers used to at least take one day a week to only use screen readers to do all of his work. He was happy with radix. I didn't go that far. Nice. Yeah, I mean, like, it's, you know, we all care about accessibility, but sometimes the reality of everyday development kind of tends to get in the way, especially in a small startup, you know, like, you know, you're moving in a rush. What these things are wonderful for is that if you use them, usually you fall in the pit of success and you make things accessible by default.
Hot Takes on Libraries and Kodaks Integration
Using libraries like MUI can lead to accessible designs by default. MUI is self-consistent and rich, but less customizable compared to other libraries. Applications may choose platform defaults for accessibility or unique designs for branding. The speaker is unfamiliar with Shadsey and UI but encourages others to share their knowledge. Kodaks works well with headless libraries, especially when building a view with React ARIA. Feedback on Kodaks can be provided through Discord.
What these things are wonderful for is that if you use them, usually you fall in the pit of success and you make things accessible by default. So that's the real powerful thing here. All right.
So we have a bunch of questions where we're basically expecting hot takes on libraries you may or may not have used. So let's do this.
Any thoughts on MUI? So I've been using MUI. What I like about MUI is that I kind of worked with it on so many different spaces. Like, I built stuff for mobile with Dart and Flutter and it was around there. With React, with other libraries. It's very rich and it's very, like, self-constant. But in terms of customizability, I would be remiss if I didn't say that they are making effort. They also have a headless part of their offering. I would say in general, I found material UI less customizable than other libraries. Just because it's so self-consistent. Stuff like the inkwell. When you see an application built with material UI, it takes a lot of effort for the developers to hide the fact that they're using MUI. If you work with it enough, your eye immediately catches that. Yeah, I guess there are kinds of applications where it's almost better to have the application look like platform defaults because it's more accessible. But then if you're branded or commercial or a public web app, maybe that's not the right choice. Not all applications require a unique design that is over the top and has to be super special. It depends on the context. But if this is what you sold to your customer or PM or CEO, that you have this app that is going to look unique and not like others, you're going to run into trouble sooner or later.
Nice. One thing that other people apparently want to know is your thoughts on Shadsey and UI? I'm not familiar, sorry. I keep hearing that name on Twitter a lot, but I haven't clicked on any of the links, so I can't help you here either. But if somebody knows about it, maybe in the speaker Q&A, they can come and educate. Yeah, please do. Exactly, yes. Tell us about your favorite library. We really want to hear about your favorite library. All right, let's see, what else do we got here? So there's a question about Kodaks. How does Kodaks work with headless libraries? Are there specific ones that it works better with, or others, or is it. It plays pretty nicely. It is a visual editor, so until you kind of build, like with the stuff like React ARIA, you build a view around it, you're better off using VS code, but once you have an actual view, it will play nicely and add fun building these little samples. Nice, well, thank you very much. I think that's all the time that we have, but if you do have more questions, you can go to the speaker Q&A after this and have a more in-depth conversation. And if you have any feedback on Kodaks, please let us know, the best way is Discord, the links are in the GitHub page for the examples. Thanks for listening.
Comments