Lesson Description
The "Components Q&A" Lesson is part of the full, React and TypeScript, v3 course featured in this preview video. Here's what you'd learn in this lesson:
Steve answers student questions regarding how to build flexible, accessible components using the as pattern, leveraging TypeScript and utility types to infer props from design system variants, and best practices for maintaining consistency across inputs, text areas, and other reusable UI elements.
Transcript from the "Components Q&A" Lesson
[00:00:00]
>> Steve Kinney: Does the as technique bring in any of the accessibility features that are already in the built-in HTML tags? Yeah, I mean, under like all it is doing is going to be instead of always being a div, right, it will be whichever semantic component you chose, right? And the nice part about whenever we do that like component props without ref, that means you get all of the HTML ones, including all of the ARIA tags, right?
[00:00:37]
If you did not use the component props like that come from React, you would be on the hook for each of these, right, and also because they've got a hyphen in them, like that's also annoying, right? You would be on the hook for each of these, but by inheriting the type and then using the spread operator here to put it on the actual like component, you get all the ARIA tags, you get like you can make that card a list item.
[00:01:08]
If you have, it is really, we actually, if you can see, I'll go back to that grand plans app. If we go in here, like we've got the create grand plan like form, the input and like submit, right? And for the plans, I'm using this component, this grand plan component, which is wrapping that card component I was just in, but it says as a list item, which means on an accessibility level, I don't have to do all the ARIA stuff because it is legit a list item.
[00:01:40]
Like it will render as an LI with all of my styles. It will look like a card, but it is in an unordered list as a list item. So it is like got all the accessibility that one would need, and I don't have to play around with like not treating it as the real. I get all the styles, I get all of like the look and feel, but I also get all the screening and stuff, and you will get everything, for instance, tab index, all of the stuff will be, you get all of the good HTML practices for free by using stuff like the component props from React.
[00:02:16]
And you can get your design system and the variances consistently because the other part that is useful, do I have it in this version? Yeah, is there are a bunch, and it's kind of what you and I were talking about the other day, where I have a like, yeah, like text areas are different than inputs, right, but they're kind of, I want them to mostly look the same. And so here I can take like the base input styles, and this is just a string of Tailwind classes that I put on both the text area as well as the input field, right?
[00:02:57]
Focus ring styles, I don't want to write this, and if I decide I'm changing the focus ring style and I want inputs and text areas to look the same, I can do this one place, right? And then with that class variance authority, I'm getting all of the variant props as well, right? Focus ring visible styles, like, you know, this changes over time, but like what is it, what do I want disabled things to look like in my design system?
[00:03:24]
There's only two classes. Hey, on in Tailwind, like show the cursor not allowed if it's got a disabled prop and make the opacity 50. If I decide that 50 was too much based on like some background color changes, I change this one place and everything works, right? And so like it's not necessarily TypeScript and React per se, but there are really powerful things with TypeScript and building a component system and building a design library, design system, component library that I think apply here as well, right?
[00:04:02]
Like I could, for instance, if I wanted to then even say that I just wanted to get default, I just wanted the keys of this input border style so I could like have two variants based off of that, I could use TypeScript in this case where I could do something like type border style is keyof, right? And so typeof is I'm taking a JavaScript object and like reverse engineer type, and I just want the keys.
[00:04:41]
So now, oh, leave me alone. Oh, there it is down here. I was looking too busy looking at the error. So now border cell is default or error, and that's effectively what that class variance authority is doing. You're like I don't have time to refactor to that, you can probably use TypeScript to reverse engineer your own version of that, right? But now I could say like, hey, a border style variant or like a property on my components is based off of this.
[00:05:26]
I more, probably better if I had done it with this value. So like export type text size, right, and now text size is any one of those, right? And so now they can be the thing in the implementation, and they can also be the inspiration for the type and the like my actual API for this as well, right? And if I just did typeof, you can see I would get this as a string. If I throw an as const here though, you get something even more powerful, which is now they're not strings because this can never change, and once you tell TypeScript it can never change, right, now it actually knows that the values themselves are locked in place too, right?
[00:06:11]
So you can start to like use the practicality of your design system to kind of like inform your type system for your components as well. I don't know why I would need the values per se, but like the keys, if I wasn't using class variance authority, absolutely. So on and so forth. You could even say like, let's say I wanted spacing and text sizes to have, they don't, but let's say I wanted to have the exact same keys, right?
[00:07:04]
I could do something like, like live coding types is going to be fun, but like I could say we could do type sizes and I could say string, let's do medium, so one of these is going to break on me, but that's fine, string, and so like anything that I do with sizes, I want it to like conform to this, so I know that my spacing sizes and my text sizes and I don't know, paddings, whatever, right? You decide, you could do something like, and it won't, and you know why, right?
[00:07:54]
It immediately told you why, right? And meanwhile I do it here and this one does. And so I can make sure that even like the tokens that I'm using are consistently applied and kind of like build some of this stuff out as I kind of go through. When you were considering patterns for polymorphic components and basically choosing, you know, to render a section or article or whatever for a dev or anchor or a button for a button, did you consider like the approach Shad CN does where they like have the as child property and they render it as a child?
[00:08:32]
I was just curious if you like with a render prop. Well, they have like an as child prop on the components, so like if they want to do a button as an anchor, they would say button as child. It's just a Boolean property as a child, and then they would just literally render the anchor as like a child, and it like uses this slot internally and forwards the props and it's all weird, but I was just curious if you had evaluated that approach and had any.
[00:08:56]
So I haven't done that. I've done something similar, which is a lot of times I think in a library like that, you'll see something where they'll have a card and they'll have like a card heading and a card body and a card footer, but they'll let you put that in any order you want, right? And have I gone down the road of like getting really ridiculous with typing the children in a way, you can definitely say I want children that have these props, but you can't say it has to be this kind of component, right?
[00:09:32]
And so what you will see a lot, this is a pattern that was used a lot more before the Context API came out for like a lot of state management, where you would see something like, and this obviously won't compile because I'm just writing pseudo code at the bottom of this file, where you'd have something like a card and you give it something like heading, and that could be like a, and it would like choose to render that component in the right spot and then you could do like body and like what you wanted there, so on and so forth.
[00:10:09]
So you weren't stuck with just strings, you could put like icons in there or other components. My suspicion is that's probably what they're doing is something, is that like what it kind of looks like or. Something called like the slot component from Radix, and then I think they take whatever props you set on the main component and just like forward it and I don't know it's some weird magic. I've always used that approach that you demonstrated.
[00:10:37]
I was just curious if you had thought with like Shad CN and stuff like that. When last time I was like fully like building out a complete design system, I was at the time was using Svelte, and it wasn't like there is a more version for Svelte now, but like three years ago, but I remember I read the entire codebase, but I never actually like used it a lot. Like I was like I read like a bunch of them like I'm going to steal this idea, I'm going to steal that idea.
[00:10:57]
I think I just didn't steal that idea because I didn't see it at the time. So I can't really answer that question, but like it seems legit. Yeah, it's one of those things I'm like should I steal that idea for my design? I have never had a problem with the as, like the as and giving you a string for the type has always worked well for me. But like they're also like it's super interesting because like a library like Shad CN like has to kind of be even more flexible, right, because it's got to be everything to everyone.
[00:11:31]
If you have like when you're building a house, you can kind of be a little bit more like this is how we roll, right? And like that's kind of, I should be more willing to use third party like component libraries than I am because like I just, at Twilio we like, at Signer we built our own. At Twitter, they had a team when we got acquired that had built one, and at Temporal, we decided to use Felt like way too early, so there was no ecosystem whatsoever.
[00:11:55]
So like I've built my own three times now and I haven't really like, other than staring at the other ones sometimes to like take inspiration, I can't say that I've like built a big thing on Shad CN, which seems super cool, and there is one for Svelte now, but I don't have that, like the requisite experience to answer that question with any authority. And that is I think that there are like, again, like this is one of those things where the peculiarities, like we're just saying, like we'll go, every design system is unique and special in its own kind of way, and every React application, again, if you're not using the design team, you're not on the design system team, you have a collection of components that get used.
[00:12:39]
You have a design system or a component library at the very least, whether or not you have one in reality. And so like figuring out where the pain points are, where you find yourself doing lots of like extra things and like can we infer those types? Can we use utility types like pull out the parts that we need? Can we take the actual code of like the object that we're using to apply the Tailwind cloud and derive the prop types from that, right?
[00:13:06]
It's like the principles of keeping your code DRY also work for your React application, your TypeScript application. And then in the day-to-day usage, the goal, right, is to get to kind of where we were in the very beginning, where in the actual, like all of like all of the squirreliness should at least be hidden down in your components, right? And like as you're going to use it, you almost look at like what we had at the beginning of this workshop, where you almost don't see TypeScript at all, right?
[00:13:38]
Because like under the hood, yes, React's pulling in all of those types, right, from its, you know, that like library of like all the props that a button has, that is like pulled into the React type library. And in fact, I think Svelte uses like JSX's like types for HTML because they're HTML components, right? So a lot of them are using those, and they're like built on all these abstractions and like because again, at the end of the day, this is electrons going through a copper wire.
[00:14:02]
You go down the stack far enough, like all the abstractions vanish. The idea is give yourself good enough ones and kind of learn how to kind of go with the flow of TypeScript, be able to like pull out the stuff you need, using that type inference as much as possible so you're not typing everything all the time, right? That you're kind of getting the checking for free and it's not like a burden, I think becomes very liberating because then you go to change something that seemed innocent and a bunch of stuff lights up red, you know that you just avoided a boo-boo, right?
[00:14:46]
Yeah, Dustin. Are there any benefits to using React.FC? OK, so React.FC, just for everyone else, was where you might do something like my component, and some of this I'm going to get hand-wavy for a moment, but I will tell you why I'm getting hand-wavy when I do that, and it will be some kind of component like this, right, whatever. This is React functional component there. You don't, there's like if you just type the props, you get the same basic effect.
[00:15:18]
There was, at least I don't think this is still the case, but there was a time when no matter what, I think they might have deprecated this React 18, don't quote me on which version it got deprecated in, would always always put like children question mark React node, which meant that if you use this, your component technically supported children even if it didn't support children, right? And so that's kind of where you see the stuff where we would have the props and we'd say like button props, like I don't think you need to do this, right?
[00:15:59]
Like it doesn't real, like if you look at the syntax, it's not like you don't put it here, you do put it here. I don't think that like there were disadvantages for a while and I think it was a breaking change in one version, but I definitely in my experience there's not been an advantage, but at various points there have been disadvantages. So like I will give a soft recommendation of don't, that I, not a hill I'm willing to die on, but my like soft recommendation is for reasons lost to time that I've since forgotten, and I think, I think that no longer do you see children for free on that.
[00:16:42]
I think that was a change in React 18, many moons ago, but like there were some like interesting pieces, but like I prefer the syntax anyway, because I also don't want to import like get another thing from React, like here I import nothing from React. Here I have yet another import FC from React, and I don't want to. So I don't think that there is advantage, there were at various points disadvantages, I don't think there's presently an advantage.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops