Tailwind is messy. Make reusable components instead with ReactJS, TailwindCSS, & TypeScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello developers welcome to defry so today I want to show you an extremely helpful skill when it comes to creating reusable components with react and specifically Tailwind um and also with typescript so what do I mean with a reusable component so instead of if I want a button you can see I'm working with buttons here just creating a button element and adding my custom content and then styling the button manually so for example I want to say BG black text white now I have this black and white text button um instead of doing that what we could also do is to define components which kind of do the default stuff for us for example I've created this button component and I can add my text and by default it will look like this primary styling but I can overwrite it with some other options I've created all of these variants that I might use quite a lot for example if I want a button that is you know some kind of dangerous or permanent action I might make it into this danger variant and if I want it to be larger I have a size property that I can set this to be large now I have a large danger button so we have all of these variant set up it's really easy to quickly add these buttons and if I wanted to let's let's say I want to make this text bolt I can even just add my own class name with Twi classes f bolt and now we' have overridden that class I can of course add all of the attributes that you would be able to put on any other button including events like on click so I would be able to run a function like alert click and now I have added the functionality to the button but uh the the default styling and the the different types of styling different categories or variants we can just select so it's really really cool that we are just hiding that behind just the buding tab and a couple of tags um and not have all of the messy styling and the conditional checks of which variant do we want which size do we want that's all hidden in this component so that is what I mean with reusable components they're components that you use quite often like the normal button maybe a secondary button or this danger or maybe a success button which is green you can just Define a variant in the component and then you just have to select them that's what we're going to work on in this video all right so if you want to follow along with this video all I have done here in this project is to set up a new react project using vit so running npm create vit and I've basically emptied the app component to really just show in H1 and then we have this diff which spaces its content out and here we're going to add some buttons uh which will be our reusable components we'll be building in this video that's really all I have so I also of course in in installed Tailwind uh just follow following the tnd setup on their website specifically for vit and react all right so that is all set up let's go ahead and create a components folder for our button. TSX component and we're going to export default function button now of course our button component will represent a button element so just a normal HTML button element but what I don't want to do is hardcode stuff in here I don't want to say like click me right and I don't want to hard code The Styling for example border and uh rounded medium or whatever so I don't want that because if I do that we don't have reusable buttons well we kind of have reusable buttons but if I now apply this button component we have this it's kind of a vague border let's make it border black so we have this black border and we have this button that says click me it looks horrible I mean I haven't spent too much time styling this um but we can now have this button component it doesn't do anything it is technically reusable because I can just add more buttons If I want to but um that's not really what we're after so the very first thing I would like to do is to be able to customize which text the button shows and the easiest way to do that is to actually make this a component with an opening and closing tag and if you pass in a value in between the tags like uh my button this value will actually be passed into the component as a prop a property called children now right now my typescript has of course no definitions for which property the button component accepts so we get the error the type Children of the type string so this string will be passed in as a children prop has no properties in common with the intrinsic attributes of the component so let's go ahead and Define an interface I usually set up interfaces for my props I'll call this button props and of course it will be an object containing a children prop and if you would read the react and typescript documentation you will know that oh whenever you pass in children that type should be of the type react node from react and a react node is really just anything that you can render as jsx it can be an element a component it's just a single string so these children will be of that type and then of course in the props themselves we'll pass in that and we'll destructure them into children which sounds really strange out of context and we'll set the type definition to be of our button props and now of course instead of hardcoding Click me or whatever we can pass in the children property and now it will say my button so now if we have made our reusable button already a bit more customizable so I can say click me and this second button says click me so this is kind of like how you would write a normal button with some content between the opening and closing tag which is quite nice now of course we need to be able to make the button do something when we actually click it and right now the component does not allow an onclick property in fact it allows none of the properties that on the button we would be able to add like uh all of these area tags or whether the button is um disabled or the ID of the button or any of the many events that we might want to check for on the button so we can't actually pass it into the components well react has a very nice interface that we can use to basically copy the type definition of all of these attributes for the button and set them as as props on our component so we can extend our own interface from button HTML attributes and this we need to pass in as a generic type the native HTML button element so what this will do is it will read the attributes from this element so this is the native button element type and it will read the button attributes and create button props out of it and we're just extending my own button props from that so now at least with typescript it will understand that we can add on click for example we'll have this function that runs an alert and we'll say clicked now right now the button doesn't do anything because all that we have done is we set up the type definition but we never use that on click so what I'm going to do is use a very cool little JavaScript feature when you're destructuring and you're destructing from a big object so these button props can be quite big right there can be loads of properties on here like on click like ID like all kinds of other stuff um but I'm not going to sit here and say oh I want to destructure on click so I can add it to the button and I want to D destructure ID so I can add it to the button I'm not going to do that for all of them so we can use a cool JavaScript feature which is the spread operator and if you do that while you're D structuring in these curly brackets you're basically destructuring the uh specific properties that you're naming like children which we use in specific places but we're basically just saying well any remaining property just just create a new object out of it with all of the remaining stuff right so this props value will just have all of the potentially remaining attributes that we pass in Via props but now on the button we can just use the spread operator again and take that props value and then the spread operator of course takes um a data type that has multiple values like an array or an object and treats them as individual values so everything in this props object is now just individually added as an attribute to the buttom so now because we're passing in the on click it will be part of the props object and we're just adding all of the props to the but Buton including that one click it will work so now my button actually accepts all of the default attributes that a button allows for example ID so if I just inspect this first button here you will see it actually does have an ID so we can treat this component as if it really was a normal button element now why would we want that why wouldn't we use use a normal button element well when it comes to styling with Tailwind you'll probably end up with quite a lot of styling code for example if I do border two for B thicker borders and maybe we'll add some padding so maybe PX one or maybe two py1 something like that H maybe something like that fine uh maybe want an hover State on here so hover PG neutral 200 so we have this kind of dark or light gray hover effect you know this can already be quite a lot and if I need multiple buttons with all of that styling you're just copying the same class list with t with classes over and over and if I want to make a change to how all of my default buttons look I have to go in all of these elements and change their style uh their classes now I just need one place where we have the styling but we're not done yet because sometimes we actually want to overwrite Styles so let's say this click me button or actually let's add another one and we'll call this delete so I want a button that deletes something now in order to make it clear this is no ordinary button it's an important button or a dangerous button you're doing a potentially destructive task I may want to say well let's just change the class name of this button because the class name of course is a property that we cannot pass in because class name is just one of the attributes that we are uh treating as properties that we're passing in as properties potentially through this type definition here so we can definitely also add a class name on the actual element and let's do like BG R 500 for example well nothing is happening just yet why is that well I can guarantee you that that class name property this is definitely a property will be passed in through the props object because we're not destructuring it and it will be passed onto the button but then we just have another class name which overwrites the previous class name now if I actually apply the props after the original class name now we are over writing the original class name so we have no border no rounded edges no padding but the props are applied so we either have to choose one or the other now there's actually an npm package so npm Tailwind merge and this is a utility function for some reason the documentation is gone let's go to the GP so till in merge there we go gives us a oh my God there we go gives us a function called tww merge and we can just pass in multiple strings with tnd classes here's the first string here's the second and if there are classes like padding and background that you're also using in the second string the second string will overwrite the ones in the first string so the actual result the return value of tww merge is it still contains this hover BG dark red because it has not been overwritten by the second string but it will have padding three because the padding two and padding one PX py has been overridden and the custom background color has overridden BG red and that's exactly what we need here let's go ahead and install this package so uh let's go ahead and npm I till wind merge till wind NOP till wind Dash merge not merger merge that's it so now what I can do is I'm just going to wrap this string in curly brackets so I can run some JavaScript and we're going to import tww merch so import tww merch and then the default styling we have here will be the first argument of the function and the second argument well we're just going to have to destructure class name from the props and now we can specifically apply that class name prop over here and look at this now we actually still have the UN overridden border and padding but the only thing we are adding as a class name prop is the background red which is simply going to overwrite whatever the default background is but it's going to merge it together with the old stuff that's already a nice start well let's say that um pretty much all of the buttons will look like this P this default primary button then it's fine to just have a few buttons that you're kind of overriding some stuff so maybe I want an slightly different delete button maybe I want a background red 500 text white border none so I don't want any border and the hover should now have a BG red 600 so now I have a completely different delete button well the problem is now I'm kind of styling my buttons where we're making the markup of our page not in the component and if I only do this once it's fine but if I need multiple delete buttons we have the same problem as before now I have a lot of ugly code and if I want to change what my delete button looks like maybe I want my uh like the font to be bolt now I have to go into all of my buttons and change the font to be bolt ideally I want to just have one button component that does this for me now of course what I could do is have another delete button component but what I am going to do is set up a component variant system where we can pass in a property for for example I want something that looks like this let just uh let's just add another button here so I want basically to be able to specify like oh variant should be the delete button or the maybe like danger variant right so then we have our primary button without a variant prop that's just the normal one but if I specify a different variant then I want it to take on the different styling so in here then we basically have to choose hey if the variant is uh danger well then we have to apply the classes for that danger variant now there is a very nice um uh npm package that will do this for us and it's called class variance Authority and what it allows us to do is Define this function where we pass in an object where we can Define all of these variants so this is the primary uh styling and this is the secondary styling and this is the small styling and this is the medium styling and all of these variants you can choose via props with your react component you can also specify which are the default variants if you don't specify them in the props um in order to apply this however we'll also need another npm package so that package is called clsx and together with t merge we get this stack of till merge clsx for some reason mpm doesn't show the documentation in the page anymore so I can't show you a code example but basically what clsx does is it conditionally chooses classes so the class variant Authority chooses or it has the configuration of which classes there are clsx will then choose which of them to use and then we have multiple strings of the variant for the small um variant and the type variant and the color variant or whatever and then we merge all of them together on top of the base styling with till with merge so it might be a bit vague with me just explaining it but let's install all of this and you'll see it in action as we apply it so let's npm install clsx and class variance Authority shouldn't take too long there we go let's go back to our page so what we're to do is go back into the button and first we'll set up these variants so I'll make a separate variable below my component called button variance and will run CVA that's the function we get from the class variance Authority package and this function will need your base class this is the class or the classes at least the string that will be applied to all of the variants and in my case the only common thing between all variants I want the border or the uh well the Border radius to be rounded so we're going to apply rounded medium that's the only common thing I will have then we'll have this config object where we will Define each variant and I'm going to have a type variant and I'm going to have a size VAR you can have multiple older ones and for example for type I want a primary type and then we can just Define what the styling of that should be and it will be basically all of this stuff here so let's cut this out and we'll actually we'll just remove this still in merge here for now just make this an empty string here so we don't get any errors and I'm just going to paste in The Styling that we have by default but now we don't have to supply rounded because it's already part of the default styling so if I choose the type variant to be primary it will be that uh button that we had before with the black border but I may have some other variants that I want like a secondary button that I may use in other situations so there we also have a border too but then the Border will be white um and actually the the padding we are not going to take um in the type that will be part of the sizing so we'll leave that out so we'll have a border white and actually we'll just hard code a BG white as well for the default one but then the BG for the secondary will be black and we hard code in text black and here our hard code in text white so you get a black button with a white border and white text instead of a white button with a black border and black text so it basically inverts the colors and then for the hover State I will have BG neutral uh 800 so that instead of being close to White we're going close to Black and you know what let's also Implement that Danger button so that will be pretty much the same stuff let's just copy all of this danger and in instead of Border two I'm going to say border none so we have no border so we don't need to specify a color the text will be white but the background will be red 500 and for the hover we'll have red I guess 600 so now we can quite easily specify the stylings for each individual type of the button for the size then we'll maybe have a small button where we have text small maybe padding x one and padding y zero which you basically don't have to specify but because we're going to do the same stuff with the other ones I think it makes sense to show oh this small one actually doesn't have padding now we have a medium size and maybe a large size size and this will be text base so the normal base uh text size and we maybe increase the padding a little bit and a little bit of padding Y and with large I think XL might make sense here and then we'll do something like um so PX maybe four and py two maybe something like that anyway so we have a couple of variants defined we are not using them in the button just yet that will come in a second so now that we have the variance defined we also want to define the default variance so if I don't specify which variant the button should take on well what should it put in the class name well we're just going to say for the type and you can see that this is already typed by typescript so for the type variant just use the primary by default and for the size use medium by default so this is the configuration of these button variants so now what we need to do is three things we're going to call in order to create a cloud name for the button we're going to call Button variant that's a function that takes in an object and in that object we we need to specify which size and which type we want to select I'll leave this empty for now we'll come back to this this will return the kind of the configuration we have here we have to pass it into clsx make sure we import that and clsx is going to then apply or choose which of these classes it needs to take based on those conditions and then we want because we then get multiple classes we get one for the primary we get the base class we get one for primary and we get one for the size we need to merge them all together with tww merge so we get this kind of weird stack of functions in order to make this a bit cleaner however what most people will do is they will create an function in a different file which will do all of this so let's create a util folder for utility functions and you'll often see people make a file or a function called CN for class name so let's export default function CN and we will spread inputs of the type class value and these come from CS X I'm not sure which one here it's kind of weird and it will be a class value array so a class value will be either a single string but it could also be an object similar to how this this kind of looks with like um uh this class and then a true or false value which is what this button variance will return so it will return true or false if it needs this class or this class or this class so it's kind of a structure that says oh we want this class and we don't want this class so these class values can be all of those things and it will be an array because we might have multiples and we store them as inputs so then all we're going to do is just stack on tww merge clsx and we'll take in the inputs in there so it's kind of like a little utility function which makes the code here a lot cleaner because now we don't have to do all of this we only have to do CN okay A little just a little bit cleaner now we now have to pass into the Buton variance which variant we have chosen and we will get that if I just clean this up here from our props now I could manually say oh the type prop is either primary or secondary or danger or whatever but luckily we can actually infer the types straight from this Buton variance variable so on top of extending these attributes we're also going to extend from uh variant props from class variant Authority and then we have to pass in the type of my button variant variable so here we've already defined the types of all of these uh variants so type could either be primary secondary or danger and that's what this is going to feed in um let's see what's the problem here button props cannot simultaneously extend types and variants ah okay so the name property type also exists of course in the button attributes because there is a button attribute called type so you know what let's just rename this not to be type but just the variant which variant of the button that we want the primary the secondary the danger button there we go it's all happy so now we can destructure variant because it's part of the props and we can destructure size there we go and we just pass them into the Buton variance uh function which is going to return those classes so variant and size and here we can actually also dump in class name and all of these will be merged together with our CN function and as you can see we have our default button without any size or primary or or a variant but here I still had that variant danger and you can see it has taken the danger variant and if I get my secondary and this is all type save so if I say types uh if I say variant is it it only allows me one of those selections and now we have the black button let's try the sizes so we'll have size of course medium is the default anyway right so medium is the default but now we have maybe SM for small LG for large and we have a small button and a large button and it's quite easy to just modify these because you scroll down and say oh large is a bit too much padding on the y axis so I might change it back to one and there we go right so that looks pretty cool and so it's really easy to just add more variants add or modify them and you don't have to actually mess around in the messy code of your component this is a nice structure it's objects so it's structure that makes sense where is what and then over here this is clean code as well so that is how we create reusable components I can pass in I can even make like a large with the variant of danger like a large red button it will just merge all those clouds together I can pass in an on click or any other event or attribute that we can pass into the buttom uh delete this might be my dangerous action okay so there we go that is how we create reusable components how cool is that so hopefully you enjoyed this video hopefully F hopefully you found this really useful let me know in the description what you thought about it if you want to see my future videos make sure you subscribe then you will be alerted uh or you will see these videos on your homepage and all the wise I will see you next time thank you so much for watching
Info
Channel: Devistry
Views: 9,315
Rating: undefined out of 5
Keywords:
Id: wg3c1Q2nzUQ
Channel Id: undefined
Length: 28min 2sec (1682 seconds)
Published: Sun Oct 15 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.