Reusing our custom server selector will result in lots of duplicate markup. Tailwind doesn't attempt to solve this duplication problem on its own, but instead encourages us to use whatever tools for code reuse we already have at our disposal. Let's make a NavLink React component to make our custom treatment easy to reuse and change.
The code for this lesson is divided into two sections, with this you can see the progress on the code.
Instructor: [0:00] We've seen how easy it is to use Tailwind's utilities to build this custom UI, but now we have all this markup. If we want to repeat our treatment, we're going to have to duplicate it.
[0:11] We can see that this gets pretty unwieldy pretty quickly. Now, unlike other CSS frameworks, Tailwind doesn't attempt to solve this problem of code duplication itself.
[0:22] Instead, it encourages us to use whatever tools for code reuse that we already have within the framework that we're already using to build our app.
[0:32] This is a React app and the primary tool for code reuse is a component. Let's come down here and create a NavLink component, and we'll just return this markup that we have from last time.
[0:46] Up here, we can render a NavLink, and let's start to fill this out. First, we'll want to pass in the href, so we can use this with different links. We'll make that a prop here, use it for our link components, href, and we'll also change this to use the prop as well.
[1:11] Tthis component needs access to the router, so we can just come up here and grab it, and then paste it in our new component. Finally, we don't want to hard-code this Discord icon inside of our NavLink component because these are going to be different. Let's pass this in as children here.
[1:34] I'll just copy this, and down in our component, we'll render children instead, and we'll just grab this from our props. With any luck, we should see our second NavLink here, and it has exactly the same treatment.
[1:55] We can see if we're on the Home Route, it is indeed active. It's tied to the URL, and so that's all working nicely. Let's go ahead and delete this link. Let's replace this with NavLink and we'll just pass in S1 for the label. We'll see the same treatment here, and this one is tied to server/one.
[2:19] Just with a simple component here, we've already solved the duplication problem with all the mark-up we have without sacrificing any of the composability or the ease of use of Tailwinds API.
[2:32] If we need to change something, we can just come and change it directly here in the markup, and that's going to propagate through all of our uses of NavLink. Now, let's come over and take a look at Discord.
[2:42] We'll see that there is a little divider here between the dashboard and the first server. Let's go ahead and add that to our clone. I'll drop an HR in here. Now, let's match the color. If I inspect this and take a look, we'll see the color is this white with a six percent opacity.
[3:07] Over in our clone, we can add a class name to this HR and this is styled using border top so we can change the color with border T and then white to make it pure white.
[3:19] To add opacity to this color, we can actually use a little shortcut here, just a slash and you'll see here there's some values like 30 percent, but we want six, and six is not included in a default scale.
[3:31] It goes down to zero and five, but because we're using the JIT compiler, we can use this bracket syntax to put in any value that we want.
[3:40] We'll just put in .06, and now we've matched that color perfectly. Discord's bar is a little bit taller. Let's use border T and then we'll go up to two for two pixels.
[3:53] The corners here are rounded, so let's make it rounded, and then it looks like there's a little bit of margin on either side so we can use margin X for both margin left and right.
[4:07] Let's try mx-1, little bit too short, mx-2 and that does the trick. Let's zoom back out, and let's start working on these actual server images. I have these images right here in my desktop.
[4:26] I'll just come and grab them and then throw them into our public directory. Here, we see Tailwind Nest and Mirage.pngs.
[4:39] Over here, back in our app, instead of just writing S1, we can drop in an image tag and point it to servers Tailwind.png. It looks like our rounding is not taking effect.
[4:54] I think all we need to do here is come to our div with the rounding and just add an overflow hidden, and that should take care of it.
[5:05] Check that out, we've gotten this working with this completely new child that were passing in here. This one has a Discord icon, but here, we're putting in an image. Our treatment is working perfectly. Let's go ahead and delete this useRouter because we don't need that anymore. Let's spruce up this page a little bit.
[5:27] I'll come to our home page. I'll grab this, and we'll just go to servers one here, call this the server page. I'll just call this server one for now, so that we can actually see this full layout. Then, back on our home page, let's just call this dashboard. Let's change general to friends. Let's get rid of these channels and let's just get rid of all this content right here.
[6:00] Now, we've got the home page, server one. Now what I want to do is make this server page dynamic, so we don't only have one, we can have many different server pages. The way we do this in Next is just to come over here and rename this one.js to a variable, we'll call it SID for server ID using brackets.
[6:23] Then, we can come and grab the router from use router. Right here, instead of server one, we'll render router.query.sid. This is going to point to this variable that is from the URL. Now, server/one works, but if we go to server/two, that works as well. Now, we can put any number here for a dynamic server page.
[6:50] Our Home button still works. If we visit server one, we'll see we've actually broken the active state. Let's come back to our app, and come down here and take a look at this router.
[7:04] If we pop open the console, and we take a look at this. We'll see that we're using path name, which now that we have a dynamic page, is actually pointing to this file right here. That's not going to resolve to the same thing in the URL. We actually want is asPath, which is going to include that variable right there.
[7:26] All we should need to do is change this check from pathName to asPath to determine, whether we should conditionally apply these classes when the URL is active. There we go, we can see that it fixed it and now we have our NavLink working again, even if I swipe back and forward or use the forward and back buttons.
[7:49] Now for the fun part, let's close the sidebar, close these pages. Up here in the top of our app, I'm just going to paste in some server data, which points to the three images in our servers folder.
[8:05] Now, for our NavLinks, let's go ahead and grab the servers and call .map for each server, and render a NavLink for each one. We'll change these quotes to back ticks, make this an expression and render the server ID right here and we'll change these to back ticks, use brackets, and render the server.image right here.
[8:38] This is specific to React. We also want a key prop here. This just lets React keep track of which NavLink is which. If we save that with any luck, we're going to have three server indicators here.
[8:52] I'll get server one, server two, and server three. Notice that, this treatment is all working exactly how we expect. Again. I can swipe forward and back. I can use the back buttons. Everything is derived from the URL State. We're rendering the images, and this is pretty cool. We're matching this thing basically pixel for pixel here.
[9:14] Again, we have not had to step out of our markup at all. This just shows the power of how productive you can be staying in the markup and really embracing Tailwinds composable classes here. Note that we haven't had to go outside of React's paradigm at all.
[9:30] The only thing we had to name was this NavLink, but we didn't have to come up with a bunch of classes like NavLink wrapper or container and explain any CSS architecture decisions to the rest of our teammates.
[9:40] This is just a good old bread and butter React component. Any React dev who's working on this project will be able to take a look at it and understand how it works. This is really the best way to manage the coupling between your classes and your markup.
[9:56] It's just to embrace whatever abstraction you already have for code reuse and you can see here we have no more duplication on this page.