All right, so now I want to resolve this hydration error. It looks like this is happening only on mobile mode and it looks like it only started happening after we've wrapped our sidebar inside of a suspense if I'm correct. So if we go into layout here, once we added this suspense, these errors started happening. So if we go and remove this suspense then these errors don't happen, right? So I can't tell you exactly how come suspense triggers those errors but I can tell you that we definitely do have some hydration mismatch here.
And here's another thing you might be wondering. Alright, why does this happen on mobile but when I expand to desktop why is it perfectly fine? How come this is working as intended? Well, that's because Inside of our store use sidebar We have collapsed false by default if I move it to true by default and try this again on mobile mode, you can see that we no longer have any errors, right? But if I expand on desktop mode, then I have the errors because you can see how there's a visible shift for a second.
So that shift is the mismatch between server-side rendering and client-side rendering. So make sure you bring this collapsed back to false because that's how we want it. So on desktop you should not be having any hydration errors but then on mobile you should definitely be having a hydration error here. So this is what I'm going to do. I'm going to ensure that specific parts of our application only show on client mode.
So on server-side rendering I'm just going to show a loading skeleton and here's how I'm going to do that. So first I'm going to create one more skeleton that we need. So go inside of the app, browse, components, sidebar, toggle.tsx. This is one of the problematic components. As you can see it heavily relies on this collapsed state which we've just confirmed that is obviously causing hydration mismatch between our server side rendering which does not have access to this and our client side rendering which has access to this and thus they create different states and that is called a mismatch between the server and the client.
Now this isn't exactly the most dangerous thing to happen. I don't think it will break your app, but still I don't want to go ahead and proceed with these errors that we have. So let's go ahead inside of this toggle component and first let's create a skeleton for it which we're going to use to display to the server-side rendering and only then we're gonna go ahead and do all of this right all of this dynamic stuff with the collapsed boolean. So let's do export const toggle skeleton and let's go ahead and let's return a div with a class name of padding3 pl6 margin-bottom of 2 hidden on mobile devices large, sorry, flex on large devices items center, justified between and full width so that's our div that's happening like right here and then what we'll do is we're gonna go ahead and render a skeleton for this text and render a skeleton for this button. So inside of here first we need a skeleton for our text so make sure you import skeleton from add slash components UI skeleton here and go ahead and give it a class name of h6 and width of 100 pixels which is an appropriate width of our text here and below that add another skeleton which is going to be our button So it's going to have the same width and the same height.
Like this. And this just by itself did not fix anything. We're still getting the exact same errors because we are not using this anywhere right now. So the next thing that I want to do is I want to go inside of our wrapper component which is obviously very problematic because again it shifts our entire layout based on this use sidebar hooks and the collapsed state which is only available in the client which means that server-side rendering has no idea what's going on here and doesn't know how to match what is expecting on the client and what it expected during server-side rendering. So this is the first thing I'm gonna do.
First thing I want to do is fix this layout flickering which is happening. So in here I define the default width is 60% but that's not true right? That's only true if we are on large devices. The actual default width is 70 pixels which is this collapsed width. So now when I refresh you can see how we don't have that layout change but now we have this weird content which is sticking out for a second.
So the next thing I'm going to do is ensure that this entire thing, our wrapper, only renders on the client and during the server-side rendering it's going to render some skeletons instead. So this is how we are gonna do that. So let's go ahead and add const useState from React so make sure you import useState from React and while you're here also import useEffect from React and I'm gonna move them to the top here. This is one of the methods which you can ensure that you only render something on the client and not on the server because this use client doesn't mean skip server-side rendering this means this is a client component you are still going to do server-side rendering on here but it is not a server component So server-side components and server-side rendering are two different things, right? So what we're gonna do is add isClient and set isClient here and give it a default value of false That's what server-side rendering is.
By default, it's not client. And then we're gonna add a use effect and here's the cool thing about the use effects they can only happen and execute on the client server side rendering has no access to use effect so this is the perfect place for us to switch this variable from false to true set is client is now true and then in here we're going to write if is client for now just return null like this and now if you try and refresh here sorry if it is not client my apologies so make sure you put an exclamation point here so if it is not a client completely skip the rendering of this problematic component and look at what happens now I no longer have any errors but the solution is kind of ugly, isn't it? Because even on desktop, our entire sidebar disappears for a second. So it kind of doesn't make sense that we built all of those nice skeletons just for us not to even see them now. But at least we got rid of our hydration error.
But don't worry there is a very simple solution for this now. So instead of rendering null what I'm gonna do is I'm going to render this exact aside element and I'm gonna go ahead and copy this default class names here so give it class name and paste those so they are exactly what you just saw here as you can see it matches but we're not gonna do this collapsed thing at all because this is server-side rendering and then inside I'm gonna render the toggle skeleton like that and I'm gonna render the recommended skeleton. So make sure you import toggle skeleton and recommended skeleton from toggle and recommended respectively. Go ahead and save this and now if you refresh there we go. You can see how now there's no flickering.
Well, I mean it is but it's from skeleton flickering. That's good. We don't want that weird layout shift. And on the mobile it looks good as well. So you can clearly see that we no longer have those errors now.
Perfect! And let's see if there's something else we can do here. So we just resolved those errors of ours but I want to do one more thing. Inside of my sidebar index we have the recommended skeleton here. What I want to do instead is also add a toggle skeleton above it.
So you kind of have to understand what we did here. So make sure you import toggle skeleton from toggle as well. What we just did here is we have two types of skeletons showing up. So the first one is inside of our suspense right here. So this skeleton will show while this getRecommended is loading and can we bring back that promise?
We can. So you can write this promise as well and you can save it and now when I refresh you can see how for five seconds I have that loading skeleton. But don't confuse that skeleton which comes from our where is it from our suspense with this skeleton from our wrapper right here. This is a different skeleton So this one takes care of server-side rendering and nothing else. So those are two different skeletons.
I know this is a little bit confusing now but you know server-side rendering, server components, client components These are kind of a new way of thinking when working with Next.js, right? One great article that you can read is the Perils of Hydration. Go ahead and Google that and read that entire article and it will do a much better job of explaining what's actually going on than I am. And even better, it shows you examples of this happening in real big websites like Airbnb. So they also have these issues and they also fix them in similar ways.
So this is not some dirty hack that we are doing here, right? You didn't have to worry about that. Perfect. So now we should no longer be having our errors and we have clear loading states here. Both during the server-side rendering and then during the actual suspense which is loading the information.
So before we end this just make sure that you remove this snippet right here and you can also store it somewhere because it's quite useful if you want to test your skeletons and see how they look. So I'm just going to go ahead and remove this for now. Great. So again, I'm testing on here. We have no hydration errors.
When I expand my screen, no hydration errors here as well. Let's go ahead and test. Oh, it seems like we do have a little issue here, which I did not notice. So let's go ahead and see what's going on with this. So when we click here, it looks like this is not collapsing back and I think that is because when we went inside of our wrapper here right here oh yes we gave it this values yes this is incorrect I have to correct myself I told you that that width of 60 which was here is not correct But it actually is because in here we control width differently So go ahead and bring this back to just be width 60 and be entirely controlled by this I believe that now we're not longer going to be having that issue, that is fixed and now let's again test our hydration errors on desktop it's fine, on mobile it is fine as well.
Perfect, Great, great job. So just one more time to recap. Inside of our wrapper we actually didn't change anything in here. So we left this exactly as it was. But we added a little isClient here.
And you can do this even prettier, you can maybe wrap this inside of curly brackets. At least I prefer it that way. Like that. So in here we take care of that hydration error, right? And we added a little toggle skeleton so it looks even better.
And here's one more information if you need it. So I purposely wrote this like this so you understand what's going on behind. But you remember that inside of our, I think it's the container component, We installed a package called usehooks. Usehooks actually has a hook for this as well so you don't have to write this every time you have some hydration errors in your project. You can actually do this as well.
You can import isuseisclient from usehooksts like that And then you can completely replace all of these codes. You can just write const is client, use is client and remove this use effect. And it's gonna be the exact same thing. As you can see here, no errors on the mobile mode. And when I expand no errors on desktop mode and everything is still working exactly as we intended.
So if you want to you can use is client you wrote it yourself first so you know how it works but I think it's shorter and it's even better to do it this way. Great, so you can go ahead and visit my GitHub if you're not sure or if you're still having those hydration errors. But also, If you cannot resolve these hydration errors for any reason, they're not going to stop you from continuing to develop this project. As I've said, I don't think they break apps exactly. Well, some could possibly, but I think this one that we had isn't that dangerous.
But still, I don't want to proceed without fixing that first because it just looks bad especially if you're gonna put this on your resume or show it to someone so this way we got rid of those errors. Great, great job!