A second and third version of this course released using Next 13+, but this course is great if you want to see another example fullstack project. We now recommend you take the Build an AI-Powered Fullstack Next.js App, v3 course.

Check out a free preview of the full Build a Fullstack App with Next.js, v2 course:
The "Button Component with Variants" Lesson is part of the full, Build a Fullstack App with Next.js, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Scott creates a Button component and uses a library to allow variants to be applied through the component's props. Variants are added for the button's intent (primary, secondary, and tertiary) and size (small, medium, and large).

Get Unlimited Access Now

Transcript from the "Button Component with Variants" Lesson

>> Well, we still got some more components to make. So before we can actually make the form for the off page and stuff, we need a button, we need an input, we need some more of that stuff. So let's do that. So we're gonna make a button component.

[00:00:14] So we go into components and make a new file called Button.tsx const button And we're just gonna default export this button. And it's just gonna be like a generic UI button. But this one's gonna use some more advanced CSS stuff that I've been kind of using it which is kind of cool.

[00:00:43] It allows you to set different variants for components based off the props that are passed in, which I think is really cool. And this is something that I was looking for. I was trying to figure out how to do, because if you don't have something this, you end up doing errors everywhere in a class name.

[00:01:02] To toggle classes on or off, depending on the value of a property, which to me is very sloppy and cloudy. So we can use a combination of this tool that you'll see and other ones to allow our button to have different variations very easily without us having to figure out the conditionals ourselves.

[00:01:23] So that's what we're gonna do. But first, let's just make our button. So if you look down here, it's really just a button that just renders its children. It's nothing special. So we'll start there. Let's do that. So this thing takes children, and renders a button. So we'll start there, very simple and conspicuous.

[00:01:50] And then because we're going to have different types of buttons, we're going to have like a primary button, a big button, a small button, form submit button. We have to expose those changes as props so we can toggle, you don't change them if we pass them in. And that's where this CVA comes in.

[00:02:08] It's such a weird name and I'll explain it in a minute. So we're going to import CVA, and we're gonna call it various props. This is for TypeScript. Actually, let's get the autocomplete from. Class variance authority. So why is it called class variance authority? The person that made it love that show the Loki show on Disney where I don't know if anyone has ever seen that show, where it's like they were like the variance authority or something like that so that's why he called it that.

[00:02:39] And it's for classes. So he's put the word class in front of it. That's literally why it's called that. It's so weird. And then he couldn't get the the package name called CVA because there's already a package name called CVA, and the guy wouldn't give it to him, so he just had to spell it out.

[00:02:55] So that's why it's called it. It looks so official. It looks like the government made this package, but it's not, it's just some random guy who really likes that Loki show and couldn't get CVA. So yeah, cuz I went down a rabbit hole, I was like, what is this?

[00:03:10] Why are we doing this? So that's why it's that. And then, yeah, we'll import FC for types from, From types for React. All right, so what we wanna do here is actually quite powerful. So what you do is you can use CVA to define some default classes that get applied to every button in this array.

[00:03:41] And then what you can say is like, all right, I'm going to make some variants of this button, and you can say, if I want a prop called intent. So my button's going to take a prop called intent. And if the value of intent is primary, apply these classes.

[00:03:55] If the value of intent is secondary, apply these classes. If the value of intent is text, apply these classes and that's on top of the default classes. And then the same thing is down here. I wanna make a prop called size. If the value of size is small, apply these classes so forth, so forth, so forth.

[00:04:20] And then here is the default variance if someone doesn't pass one at. So this is literally the same thing if you went and just hit turn area everywhere. If prop dot size equals small, like, it gets so sloppy. So we're gonna do this. And really, if you were looking at CVA, it's just a function that just concatenate strings, it doesn't do anything.

[00:04:43] It's just like a string manipulation library. It has nothing to do with React or classes. It just combined strings, which is really all you need. So let's do that. So we'll say button classes is CVA with NRA like that, those are default classes like that and then we need to make some variants So we'll say variants, and then for the variants, I'm gonna have intent.

[00:05:25] And I'm gonna have one called primary. And for primary, I wanna apply these primary classes, so background-violet, text-blue, border-transparent. These are all Tailwind classes. And this is why I think Tailwind shines when you do it like this. You can make a whole component live like this with CVA and Tailwind, maybe pretty cool cuz I would not wanna write all of these inside of JSX, plus turners, plus if statements, I just don't wanna do it.

[00:05:54] So we got that, and then for secondary we're gonna have these. So intent is secondary at these. So instead of being a purple button it's a white button. And then at the text is or the intent is text, then It's just basically transparent. And then last one is the ability to manipulate the size.

[00:06:30] So I can go down here and say okay, I want to know the property called size and the value of small. I want to apply these classes. So text medium y-1 which means padding y-1 on both, which I think is actually two pixels. I don't know Tailwind's number.

[00:06:51] And then for medium I'm gonna apply these classes. Pass in medium button, And for large, I want to pass in these classes. And lastly, we just need a default variance. So default variance. Let's just have the intent default to primary and we'll have the size default to medium Here comes the magic.

[00:07:36] So what we can do with that is mix it in with some TypeScript magic and create an interface that extends basically an HTML element. That's what this HTML button element. And we can derive the type of all these variants by using typeof inside the variant prompts as a generic.

[00:07:59] So this is something you can do in TypeScript. Is you can do typeof and TypeScript will derive a type from that which is really cool. So I'll just come down here and do that. You don't actually have to put anything in here for these prompts unless you have some other props that this button is going to take that are unrelated to things that change the classes.

[00:08:26] You can you can define them here. But note that things like unclick type, all that stuff is already part of the HTML button element. So it's probably already there. So let's do some custom prop. That's not already part of a button. That doesn't have anything to do with style, then you don't need to add anything here.

[00:08:42] But if there was, you would add it here. Okay, and then from there, I prefer to type check my components like this using functional component. Some people like to do it after the object here. I'm gonna do it here beforehand. So I'm gonna say FC, and then I'm gonna say ButtonProps, like that.

[00:09:11] So now we got our ButtonProps. And lastly, we just gotta apply them. So to do that, we can go to the button, we can say className We can say button class. Is that what I called them? Button classes. Button classes like that. And then we've got to just pass in our props.

[00:09:40] So we can map the intent and the size and the class name that we destructure. And then we can just spread over the rest of the props, right? So we'll get the intent. And you can see it's auto completing because we passed the props, the size, and what was the other one?

[00:10:02] Size, intent. Size. It's another one. Yeah, class name, duh. And then we can just do that here. We can say this is the intent, this is the size, here's the class name. And then just spread over these props, the rest of them. Which I did not name, so let's do that.

[00:10:23] Cool. So I think this is a really good system to build out UI components using Tailwind or class-based styling, in my opinion. Which if you use Next.js 13, you can't use component libraries like Chakra because they don't work on server components. So you're kinda stuck with class-based styling for now, for the most part.

[00:10:54] And, Tailwind's amazing. So, yeah.
>> Mark.
>> You have any opinion on whether prop types should be defined as type aliases, or interfaces?
>> I don't have an opinion. I've always done them as interfaces. I don't know the differences between using them as a type alias, or an interface.

[00:11:17] For me, I've always used them as interfaces because I think when that transition period happened from like class based components to functional components. Everybody was using interfaces so it kind of just stuck with me, but I feel like types might be a lot more flexible and in that sense of making, I can see if I was making a component library, I might use types because of the flexibility of how you can extend them and do unions and things like that across types.

[00:11:49] So it might be more flexible there, but for the most part, I'll just use interfaces.
>> When do we start to bring in use client? Is that going to be in the pages? Or can we bring that into components like this?
>> You can bring use client whenever you want.

[00:12:03] Can I probably put in a use client here? Yeah, it works. It's just a client component now, but it'll still give you the same result. It'll still show up on the page and do its job. But just like if you've watched the v2 course of Intro to XJS, I mentioned in there that unless you're doing some type of DOM only API, some type of state management, some typeof, mostly that like a hook or something, you should almost never use the client component.

[00:12:38] So looking at this button, you gotta ask yourself is something happening here that can only happen in the browser. And that's the case, you gotta use, use client. But even then, you would probably get an error on the server, component if you tried to like if this as a server component.

[00:12:55] If I went into here and did window like actually, if I went in here and did window, it would just break. Unless for some reason my node environment was mocking window out which it might be. So I think some of the newer versions of node might do that.

[00:13:08] But you get the point. It won't work on the server, because you tried to do something on the browser. So I would say just always use a server component until the server screams at you or until something unexpected happens you're like that's supposed to do this and then it's like that should have been a client component so that way you don't have to think about it.

[00:13:25] Let the framework tell you.