Dark Theme in NextJs 13 – Using React Context in Server Components

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to add dark and light theme to next just 13 and we're going to learn how to use react context with react server components hey friends welcome back if you're new here my name is Hamed and here on this channel we talk mainly about react and xjs so let's get into this I've cloned down this next starter kit repo I'm going to include the link in the description you can just use this as a template create a new repository for yourself it's just an extra starter kit that's using telling CSS I've also added some previous config to it to make you know your tail and classes look nicer but besides that it's just a bare bone extras application once you clone this by the way you have to go in PM PMI to install the dependencies I've already done that once you have installed the dependencies you can just go pmpm Dev to start the development server and we're going to build this from scratch together okay so let's open up our page to have a look at what we're dealing with over here so if you have our outermost layout this is going to be the layout or of our whole application this is what comes in built in when you run pnpx or the create next app script and my repository is actually bootstrapped with create next app so it's the same thing you're going to get this layout which is the layout shared across all of the pages of your application then you have this home page which is just representing your home page you have this next just to starter kit now to add theme we're going to use next themes package I do have a video actually adding this theme to next.js Davos for an extra S12 and now we're going to use the same package to add light and dark theme to Nexus 13 and the only difference is really knowing how to share context or provide context inside of your react server component because next theme or the way that it works is that it requires you to wrap your whole application this theme provider which is a react context provider that comes from this next themes package now Nexus 13 kind of shaked the way that we built react applications in the sense that in the app router which is built on top of react server components your pages and layouts and components by default or react server components and they're going to be running on the server reacts every components don't support react hooks or react context which is a hook and so anytime that you need to use a react hook you need to use a client component and the way you create client components inside your app router is that you would put in the use client directive up top which we're going to see together now let's start by actually installing this next theme packet so I'm going to stop the dev server here I'm going to go pmpm at next themes let me restart the dev server over here and now the way that you would go about sharing a context uh with your next.js 13 Apple inside the app router depending on where exactly you would need that context you can decide where in your component tree you're going to create that react context to provide that context to its child pages and components now for something like theme or something like authentication typically would need your whole application to have access to that context so if any component wants to hook into it or if it's changing something like in this case a theme app wide you need your whole application to be wrapped with that context provider and the way to do that is typically by creating a provider's component over here so and then exporting a component from here let me just rename these to providers and what we're going to do here is to first of all up top you're going to indicate that this is a client component because if you we want to use this context provider that comes from next themes we know this is using react context provider under the hood and what these providers component is going to do is is just to wrap the children that you pass to it with this theme provider let me actually just use this down here and we're going to wrap anything that you pass to this or there's a specific provider now you can add any other providers that you want to disk providers component that you have created here too so for example if you have an authentication provider or any other Library really that needs to share some context within your client component if it needs to be app wide so you want it to be from your outermost layout down to across all the pages in your application you can include it in this same providers component and then you can come back to your layout and say okay now I want to wrap all of my pages with this context provider so I'm going to get these providers component that I just created and I'm going to wrap my entire application with this provider function and so now all of our pages and nested layouts are going to be wrapped with this provider's component which just shares this theme provider that we got out of the next theme or any other context that we wanted to kind of bring into this component so that our components and Pages nested deeper down in the tree can hook into this same context to get what they need to use here now you can also wrap the body tag if you want to with this providers for now I just needed my layouts to be wrapped with this so I'm just going to create a header component and a main component and also a footer to have a proper layout here so I'm going to just plug this children component which is going to be different pages or nested layouts if we have and let's say in this header I want to create a navigation and in honored list With An Li inside of it and maybe a link just so we have something up top in our navigation that goes to our home page and let me also bring the next link component here now going back to our site we should be able to see that home link up top added to the header you just add some classes to the setter so it's nice and centered as you can see here I just brought it Center with some container classes from Tailwind now the idea for me is that I want to add a button here that allows me or the user to switch to themes now the way that the next theme package actually works is that it reads your system preferences as well and then it also allows you to um select a light or dark theme now the way it works is that it would add a data theme attribute on the HTML element you can actually change so that instead of a data attribute it actually adds a class so if you come down in the API section for the theme provider you can see here you can pass down an attribute to your theme Provider by default it's going to be data theme so it's going to add data theme attribute to your HTML tag but you can change it to class so that it changes or it adds a class attribute to your HTML element and why would you want to do that because we're using tailwind and with Tailwind if you go to the documentation here search for the Dark theme so you can use tell me CSS to style your sites in dark mode and the way it works is telling out of the box comes with this dark variant and the way this works is you would prefix this dark variant to your classes and so it will only apply this class if the theme is dark and the way it understands if the theme is dark or light is by this dark mode attribute that you can pass into your Telvin config so here we set dark mode class which looks to see if a class attribute is added to any of your parent HTML elements such as your HTML or body and then if so it's going to apply this dark variant and here is an example here so here you can see the HTML and body tag and we have a div with a background of white and then a dark variant that says the background should be black now here we don't have any Dark theme but if we now set a class of dark on the HTML tag which is a parent element to this div that's using this dark variant it doesn't need to be on the HTML element can be on the body or any parent element to this div and then tell me would read this class and actually apply this dark theme now when you want to enable theme inside your whole application it's better to be on the HTML tag so anything inside this body tag it's going to actually read to that attribute so therefore Telvin can apply these dark variants to the whole document and the way the next theme works because of this attribute that we can pass in and say hey instead of setting the data theme attribute set a class attribute so next theme is going to set a class attribute on our HTML tag Kelvin CSS it's going to read that class and then apply or dark variants this is how it works I don't know if you cared for it or not but this is how it works behind the scene so let's actually go to our providers First Things First let's just pass this attribute and say hey we want you to add a class attribute not a data theme attribute we're going to also go to our tailwind and say hey for the Dark theme actually look at the class attribute okay so let me save this never go back to the site nothing has changed but now that we have this context provider we can actually go back to our root layout and add a button here that allows us to change the theme so let's actually go ahead and create this component or this theme button together I'm going to create a new folder called components inside of it I'm going to create a theme button and what we're going to do here is we want to return a button and so we're going to use this button as a toggle that allows the user to toggle between light and dark theme but for that we need to know what the current theme is and then change that theme now for this the next theme actually gives us a hook this Hook is called the use theme Hook from the next theme package and because we want to use a hook we have to turn this into a client component by adding the use client directive up top I'm going to get rid of this react and then this is going to give us the resolved theme and actually a function to set the theme so what we're going to do here is we're going to say if the resolved theme is dark will show the light button inside of this button and otherwise show the dark so it's just going to be a light or dark text inside of this button for now we're going to add icons to it later on but let's actually go back to our root layout and bring in this theme button so we can see it on the site I'm going to import this here and now we should be able to see that text dark or light because we are on a light theme right now it's showing the text dark but if I actually click on it nothing happens because it's not hooked up so let's just go back here and what we want to do here on this button is to have an on click Handler and we want to actually call this set theme function that we got from the next theme package to toggle our theme so if the resolve theme equals dark again I'm going to set the theme to light otherwise I'm going to set this to dark let me just save this and now if I click on the dark it is going to turn the site to dark and if I click it again it's just going to turn it into the light mode now to make this a bit nicer I'm going to actually install the hero icons package so I can bring in some icons so I'm going to stop the dev server and actually add as a Dev dependency the hero icons package that has some nice icons this is from the Telvin team so let me restart the dev server and just to style this a bit nicer I'm going to replace this button with this classes that I've added so let me also import these two icons down here and all we're doing here is added some classes to this button if you're still having that on click that toggles the theme using this set theme function and then instead of just rendering a dark or light text I'm rendering these two icons depending on what my resolved theme is now refreshing my application should actually bring in these two buttons or icons so as you can see this text updated to reflect these different icons depending on the current theme okay so this is almost there but a couple of things to keep in mind going back to the next theme package they recommend rendering this toggling switch or this UI only when the page is actually mounted on the client to avoid mismatch or hydration mismatch between the server and the client so therefore we should only render this theme button if we are on the client side we could use use State and use effect to actually resolve that so let me just implement this by using user State and use effect so we're going to say mounted and then we're going to start from false and then we're going to have a use effect and this is going to set the mounted to true and we only want to run this once and let me just save this and so if we are not mounted we want to return null from this theme button and if we are mounted we will render our logic over here and so if we go back to our page now everything should work the same but if you actually go ahead and open up your developer tools go to console you should be able to see this warning that says extra attributes from the server class and style so if you go back to our elements and inspect we could see this HTML right now let me just make this again bigger there's a class set on it and a style set on it this is what next theme is doing to set this class attribute so Telvin can read this class and then apply different variants but the problem is next theme doesn't know what the theme is on the server it only does this on the client so therefore there is a mismatch of these attributes between the HTML that initially came from the server because remember this layout is a react server component which means it runs entirely on the server and generates the HTML and sends that HTML to the client so when react actually rendered that HTML on the server there was no class attribute set within from the next theme package because it doesn't work on the server and once we got that HTML on the client side that we started hydrating that same HTML with the react code next theme actually gone ahead and set this attribute so there is a mismatch of attributes between what the server sent and what the client is actually showing right now now there's two ways that we can resolve this hydration mismatch problem the first way is to actually set these attributes on our HTML tag inside of our root layout so therefore when the HTML is generated on the server it's going to have these attributes we can default to the light theme and then on the client side next theme is going to actually read the system preferences and if it's light it's going to just keep it like that and if it's dark it's going to change them but at least we have the attributes on the HTML tag coming from the server so what I can say here is that I'm going to pass a class name I'm going to default this to light I'm going to also pass a style tag and I'm going to set the color scheme over here to be light now with this if I save go to console and actually reload the page that hydration mismatch warning should be gone so that's one way of doing it the second way of doing it is a prop that you can pass into your react components is called suppress hydration warning now this is going to disable that warning if there is a mismatch of attributes on this specific tag and any text content below it so it's going to go one level down you can go to react documentation to read about this there's sometimes impossible to have the same values of the text content or attributes from the server and the client and you can use it so it's not recommended to start using it everywhere it's only for the use cases that you can't really match the attributes coming from the server to the client this being one use case because we don't know the theme from the server but I like the first solution better by just passing in those specific class and attributes that you're expecting the next theme package to add to our HTML right off the get go inside of our root layout so there isn't any mismatch then react actually hydrates the application on the client side as we saw together that's a wrap for this lesson folks we added Dark theme to Nexus 13. we learned how to use react context inside react server components and this is really a pattern that you can apply to a lot of third-party packages in the react ecosystem that depend on react context to share functionality so your client-side components can hook into this context and use the functionality that library is intended to bring on in this case was a theme Library can be authentication it can be UI doesn't matter the pattern would be similar if you need to share this context throughout your whole application you can bring in this provider's component we saw together in the root layout if it is something that you needed further down the tree you can just bring in this provider anywhere in the tree that actually uses this context or needs this context if you have any questions hit me up in the comments if you're interested in learning nexjs I do have a course which will be launched in a week or two there is a link in the description check it out and let me know if you have any questions and I'll see you in the next one bye
Info
Channel: Hamed Bahram
Views: 13,723
Rating: undefined out of 5
Keywords:
Id: RTAJ-enfums
Channel Id: undefined
Length: 19min 56sec (1196 seconds)
Published: Sun Jun 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.