Add Variants and Loading to your Button! Build a React Component Library with Tailwind CSS & Next.js

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in the previous video we put together one heck of a great looking primary button but you can't use a primary button everywhere it's the primary button it's meant to show the key action on any given page it's visually heavy to indicate that let's expand on this button that we made and make it more universally useful to do so there are three things we want to add one forward ref so it plays nicely with other components and libraries two a loading state and three variants using class variance Authority let's Dive In forwarding the reference is a common thing to do on components that you're frequently passing to other components or into libraries it allows those other components to hook a reference into your functional component so it has more control over it and can reference it adding a forward ref isn't hard but sometimes the syntax can be a little interesting to add the forward ref you simply wrap your component in the forward ref call and then make sure you're passing through the props and the types correctly here we're calling forward ref and letting it know that this is an HTML button element with props that we've defined up here by defining all the types here typescript will know what the props are and what the forward and ref type should be as well what the compiler is complaining about now is that our button no longer has a display name because the functional component itself has been turned into an anonymous function there are a couple of ways to fix this and you can decide which way is best for you but I'm going to give you my recommendation one way would be instead of using an arrow function use a named function like this however this means that you're defining a function button and also that's the name of your constant so the inner definition is shadowing the name on the outer definition to fix that you could just name the button something else but now you have this weird naming problem and instead of naming one thing you're now naming two things also the name you pick here is going to be the name that shows up in the react Dev tools when you're debugging so you really want the name to be well exactly what we're calling it over here button the last method and the preferred and best way is to use the property display name on a component this is exactly what it's there to do here we're using it to set the name button to be button and then because I change the function definition go ahead and make sure you export it as well next we're going to go ahead and add a loading state to our button adding the loading state to a button is useful because you're frequently going to use a button with things like form submissions or something that has an asynchronous action you want to inform the user that yes they clicked the button the thing is happening and now we're just waiting for the response by controlling the loading State and disabling the button you stop the user from doing a double submission and also let them know that the submission was successful and is processing the exact loading component you use can be different than the one I'm going to use feel free to spice it up or to use something that's specific to your application for now I'm keeping this simple but we'll play around with a couple in the future there are two ways you can design this API one can be very jsx oriented where the user passes in the loading component as a child the other is a little more prop focused where the user sets a loading property to be true or false either way works fine and you might have a preference I'm going to go ahead and start by controlling all of this with the property loading on the button this will allow me to very easily show and hide the loading state within the button and also show or hide the children if I want to first I'm going to define a loading component this loading component is a simple square that has a border and we're using Tailwind to spin it next I'm going to add a loading property to the props so I can pick it out of the button here and then we're going to add in the children here and change the definition to show the loading State or the children and now to test we're going to add a loading State into our testing page and now here in our grid we have one button that one clicked does nothing and the other button that when clicked shows the loading state now you'll notice when you click it the entire button size and shape changes because the children have changed and the new child is much smaller than the primary words that we're using to fix that instead of removing the children from the Dom tree we can simply hide them with changing their opacity and then when we test it the size mostly stays the same now although we want the loading icon to be in the middle so we'll use absolute positioning and item Center to make sure the loading icon is in the middle awesome now we have a loading state for our button you can also hook it up to the disabled property if you want to disable it alongside with the loading state it's time to add variants to our button the other two were pretty quick this exercise takes a little bit longer not only do we have to create several different types of buttons but we also have to tweak the CSS to get it just right for each one a button is never one size fits all for your entire application there are lots of different places where you're going to want to have a button that has a slightly different look and feel the key is to define the variants you want to use in one consistent place and then you can use them across your application instead of making every single button a special snowflake you have a consistent design language that you're using it's a great idea to come up with guidelines for where and how each of these variants should be used throughout your application if you're just building this on your own that's probably all in your head and that's fine if you're working on a team coming up with these guys lines and putting it into code might be a great idea here our button currently represents the primary button of an application it's the most exciting and colorful button but it's also the one that's probably used the least here let's set up class variance Authority or CVA and then go ahead and move our primary button to it then we'll go ahead and add a secondary button into our code on my website I've also added in the destructive link and ghost buttons If you want to look at the source code and get a good jumping starting off point for whatever you're building but because it's mostly just playing with CSS and coming up with the design language I'm not going to add it into the video click a link below to go check out the source code the first thing we're going to do is import CVA now we're ready to Define our variants the first argument to CVA can either be a string or an array I like to use an array and break up all of the classes individually to make it easy to read the first argument are the base Styles these are the ones that apply to all of the buttons the second argument Define the variance that we're going to be using here we're defining the variance and we're defining what the variants are called for example you might have this be intent or you might use the word variant you might also have something like size which determines how big or small the button should be I've looked at several different libraries across the ecosystem and the keyword variant seems to be the winning property name for defining the style of the button so we're going to use it here we've defined the names for all of our variants and then we Define another type of variant for the size and then CVA gives you a nice way to define what the default should be unbelievable all right with our variants defined now we have the exercise of taking the CSS we defined for the button below and moving it up a lot of these can be moved up as base Styles and then the rest of them need to be moved up as part of the primary variant and probably the default size oh that was fun when building this out you want to have as many common Styles as possible in the base Styles and then the variant specific ones into each of your variants here things like the background color text color and how the button should look with those colors are all defined in the primary variant this will make it easier for us to Define secondary variants and other variants where we can now inherit the base styles to use our variants we want to update our props so the user knows what variants they can pass in and then make sure we're taking all those classes and passing them to the class name the other thing you want to make sure you can do is you want to use Tailwind merge to take whatever class names the user might pass in and allow them to override the ones in your button I actually got this wrong in my last video and I'm sorry about that Tailwind user classes that are passed in after yours don't override the ones that come before it the class order doesn't matter for CSS styling instead what matters is where they appear in the style sheet and because Tailwind defines all of that on its own later class names won't necessarily override earlier ones however using a library like Tailwind merge will allow the user to override the class names that you have defined let's go ahead and import those and what we need for the props and wire it all up here I'm renaming props to be button props so when we export it we don't need to rename it and have it conflict with other components you're also exporting the types we're defining it to be all of the button attributes as well as the variant props of the variant we just defined lastly of course keep your loading state you can then pull in those props and then call the variant function with that information it also allows you to pass through the user class name attributes arbitrarily and it'll append those to the end and then we can use Tailwind merge to merge it all together before we go ahead and test this we need to add in the sizes this mostly is just changing the font and the padding to make sure the button looks okay at different sizes and now we can go ahead and lay out these variants and see what it looks like and there we go now we have our different primary buttons all defined we have a small one a normal sized one one that's disabled what the loading state looks like and finally a large button and I must say they do look pretty sweet to Define our secondary button we want something that's much lighter and look and feel instead of you watching me type all the CSS here's what our secondary button is going to look like nice the exercise to build out the rest of the variants is basically the same adding in the classes you want and experimenting with the look and feel congratulations now you have buttons with different variants that you can use in every situation in your app there are a couple of very small things that still bother me about our fantastic buttons they're around accessibility and how the mouse interaction and the keyboard interaction don't quite line up this is where most people stop in fact I think this is where you should stop this is a great great button that you can use throughout your entire application but it's not where I'm going to stop next video is there anything else you'd like to see for these great buttons go ahead and drop a comment below and until next time happy coding
Info
Channel: Build SaaS with Ethan
Views: 5,233
Rating: undefined out of 5
Keywords: next, nextjs, next.js, react, software, coding, software engineering, components, component, button, component library, shadui, shadcn, tailwind, tailwindcss, cva, class variance authority, tailwind merge, primary, secondary, ghost, link
Id: HrrZA4GmGCw
Channel Id: undefined
Length: 16min 30sec (990 seconds)
Published: Thu May 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.