Transcription
Next.js introduces a lot of new concepts: server components, server actions, suspense and streaming, static and dynamic rendering, and much more. So, it's very easy to make mistakes, even if you're an experienced developer. In this video, I want to help you fix those mistakes, and to do that, I have a very simple example that I will use throughout all of the mistakes that we're going to talk about.
I just have this one homepage, right? In the world of Next.js, if you want to have a page, you create a `page.tsx` file. I just have one. And what do I have on this page? I just have this `H1` "My Store," right? That's just this.
Then I have this `Product` component, which is a server component. What can we do in a server component? I can mark it as `async`, and I can actually fetch some data without `useEffect` directly here in a component function body. It will give me a result here; I'm parsing it as JSON, and that's what I'm outputting here. I'm just taking its title, right? So, this is just some dummy API that I found online. It will just give you some random product data, and I'm just taking its title, right? So, for ID number three, that's what we see here, right?
This is something you can do in a server component. This would not work like this in a client component. Don't worry, we'll talk all about that. And actually, the homepage itself is also a server component because everything by default in the Next.js App Router is a server component.
If we take a look at the other component that we have here, this is a `FavoriteButton`. This is actually a client component. So, if I open this up, you can see it's a client component because here we need some interactivity. I want to be able to click on this, and when I do that, the state should change, and based on the state, we are rendering something else, right? So here, the user needs to be able to manipulate this, so this needs to run in the browser, and therefore, we are marking this as a client component.
Now, to really understand Next.js, you do need to zoom out a little bit, so I will use this diagram as we go through the examples as well, right? Next.js has both a client side with client components, as well as a server side with server components, server actions, and these route handlers. I've seen beginners make mistakes in pretty much all of them, so we'll review all of these as we go through the example. So, let's get started.
Now, if you're building a product, you're going to need analytics. So, let's say I have a dashboard here, and in this space of my dashboard, I want to have some analytics from a chart so that the user can gain relevant insights. I can include one line of code, and what I will get here is the following. This is made possible thanks to today's sponsor, which is Semafor.
Semafor is a more sophisticated solution for analytics. You can integrate it directly into your own software or app, and you can customize its look so it looks like it's actually part of your app. And let's say your user only wants to see orders that are using same-day delivery. I can click on this and filter all of the data. You can see all of these cards automatically adjust to what I just clicked, right? So, that's pretty slick.
Now, your users will want to customize pretty much everything here. So, what they can do here with Semafor is change the theme, but also everything else like the order and layout. Let's say I want a little bit more like this, make this sit more like this. All right, but they can also customize each card here individually. So, if I click on the icon here, I can see all of the information for just that chart. Here, you can see the query for the data of that card. I can change the data source; it can come from anywhere. I can even write my query in natural language, and then with Semafor, I can convert this with AI to a proper SQL query. But let's say the only thing I want to do here is actually just change this from a donut chart into a bar or line chart.
Now, after I made changes, I can approve the changes, and now you can see I customized that card. So now, after reordering things here and changing this card, I have a different view here, or what Semafor calls a "lens." So, I want to save this particular view. I'm going to add this lens here, right? Different order and a bar chart. I'm going to save it. So, next time I come back, I still see this default view or lens, but now we have the third icon here. I can click on this particular lens that we just saved, and now I get my view back, and I can also set this as the default. And all of the filtering and things like that still work as well, right?
You can add Semafor with a single line of code, so I would say check it out for your analytic solution, whether you're building a SaaS or e-commerce platform, financial or healthcare type of software, or any kind of software really, and you want to give your users a sophisticated analytic solution. The features that I just showed you were just the basics; you can do much more. So, I would say check out Semafor's website. You can find a link in the description, and make sure you sign up for the waitlist. And they are, of course, today's sponsor.
**Mistake Number One: Putting the `use client` directive too high up in your component tree.**
An example of that would be the following: here we have our page component. This is a server component; everything by default in Next.js is a server component. So, here we have our `Product` component. If we go in here, you can see this is also a server component. We're using `async` here; that's only possible with a server component. And then we also have this `FavoriteButton` here.
Now, previously I showed you that this was a client component. I changed it a little bit, so now it's a server component, which means that now if I want to click on here, nothing happened. Because if we actually want to run some logic here when the user clicks, we need to be able to hook into that click event, and that's not possible in a server component. A server component only runs on the server; there is no click event there. The click event is happening here in the browser, in the client.
So, what I want to do here is I want to say `onClick`, and then I want to change some state. So, I also want to use a React Hook here. Whenever you want to hook into an event or use any React Hook, whether it's `useState`, `useEffect`, `useRef`, any React Hook, or any event (`onChange`, `onClick`, etc.), any of that, you need to make it a client component. So, if I save here, I will get an error because this is a server component still. Everything by default is a server component; we haven't made anything here a client component yet. So, you will get a warning because this and this is only possible in a client component. All right.
Now, what I've seen some people do, and maybe they're being a little bit lazy, is they think, "Oh, I get this error. I need to make something a client component. You know what? I'm just going to add `use client` right here. I'm just randomly going to put it somewhere here." So, here I added it to my page component file.
The way it works with this `use client` directive is that now this component here, this `Home` component, will become a client component, and then also everything that you're importing also becomes a client component. So, now this `FavoriteButton` is a client component, and so the error is gone because indeed you can use React Hooks as well as hook into these events in a client component. So, everything here is working.
But because we added this up here, also this `Product` component is now a client component because we're importing this into a file with `use client`, right? So, this is like a boundary, right? So, this `Product` component, now if we take a look here, if you remember, we were marking that as `async`, and we were directly using `fetch` on there. If I now open up the console, you can see that we are getting a warning here because `async await`, the way I'm using that there, is not supported in a client component. So, this `Product` right now is a client component. I'm using something that is not possible, and so you will get this warning.
This error is often caused by accidentally adding `use client` to a module that was written for the server. Well, that's not what happens here. I did not add `use client` to this file; I added it to a component that's sitting a little bit higher in the component tree, you could say, this one right here. And the way it works with this directive is that once you put it at the top of a file, all the components in that file will become a client component, but then also all the imports.
And the problem with that is that not only is it not possible, you lose all the benefits of using a server component. We want components to be server components if it's possible, so you don't unnecessarily want to make components client components if it's not necessary, right? Because server components have benefits: you can fetch data like this, you can work with secrets, you can keep large dependencies on the server. So, if you're using a third-party library that's big, you can keep it on the server. But all of that disappears if you make it a client component because all that code will be shipped to the browser, to the client. So, you don't unnecessarily want to make components client components.
So, just to show you here, because it's so important: if you have a tree of your imports, because it's about imports, and so you have a page component that's importing these two, and then they also are importing some other components. If this number three here needs to hook into some event in the browser, you just want to add the `use client` to that component file because that's the only one that needs it. Everything else can stay a server component. If you're careless about that and you make that page component a client component, which is technically possible, the downside is that now these two become client components because these are being imported into that file, and that's how it works. It's based on import. So, these two become client components, but then they in turn are also importing even more components, and those automatically also become client components, right?
So, this `use client` directive is like a boundary. So, based on the import, right? So, based on how you're importing things, everything that is being imported will also become a client component, and in turn, if they're importing something, those all also become client components. So, here, component three and four would also become client components, but not component five because it's not being imported in this component.
So, here we don't want to do this. You actually rarely want to add it to the page component file because this is so high up in the tree where you're importing usually a lot of other components. So, this is rarely what you want to do. You want to keep them usually at the edges of your component tree, the leaves of your component tree, right? So, typically for buttons, for input fields, just the small little pieces on the page that actually do need interactivity. So, in this case, it's only this `FavoriteButton` that needs interactivity. We're going to ship this one to the client; everything else can stay on the server.
Now, if I refresh, that error is gone. But wait, it's not the end of the world if a component becomes a client component. It just means that this component will run in a client as well, so the code to make that happen has to be shipped to the client as well. So, it will increase the client-side bundle, but it's not the end of the world, so don't be afraid to use `use client`.
**Number Two: Not refactoring for a client component.**
So here, let's say on the page I want to add an "upvote" button, and I want to hook into that click event. Now, we just saw that's only possible with a client component, right? The click event happens in the browser, so that component needs to run in the client.
Now, I didn't create a separate component for this; it's just an element right now on the page together with everything else here. So again, you may think, "Well, I have to add `use client` now to the top of the file here." But now everything here becomes a client component: `Home`, `Product`, `FavoriteButton`. So, all of this now is affected by just this little element here.
So, what you want to do sometimes is simply create a new component, and that's where I'll have the JSX markup. And then here in this file, I will have `use client`. And then I can use that component instead of this; I can use it right here, and I can remove this directive here. So now, the page component and `Product` are still server components, and now just the `FavoriteButton` as well as the upvote button that we just created are client components. So now, it's more confined to the edges of my component tree, just where the interactivity is needed.
**Number Three: Thinking that a component is not a client component because it doesn't have `use client` at the top.**
So, if we go back to our previous example, here we have that `FavoriteButton`. Now, if I open this up, you can see we needed to make this a client component, and so we have `use client` at the top. If we don't have that, we get an error here.
Now, in the real world, what may happen is that this `FavoriteButton` may be sitting actually in another component, let's say a sidebar. So, it's not sitting directly like this on the page; it's in a sidebar. So, we may actually just have a sidebar here, and in the sidebar, you just some sidebar component is where we have that `FavoriteButton`. Now, this sidebar may actually already be a client component.
Now, the way it works with `use client` is that all the imports also become a client component, right? So, here now I can actually remove it from the `FavoriteButton`. If I save here in both files, I get rid of the error, even though the `FavoriteButton` does not have `use client` at the top. I'm using `useState` here `onClick`. I don't have to add `use client` to the top here because it's being imported in another file that already has `use client`.
But what some people do is to see if a component is a client component, they open up the component file, and they don't see `use client` at the top, so they think, "Oh, it's a server component." Well, not necessarily, because if it's going to be imported into another file with `use client` at the top, it's going to be a client component. So, this technically works, but in my view, it would be a little bit better to simply add `use client` at the top here because this component always needs to be a client component, right? We shouldn't just rely on it being imported in some other client component. We want to make it inherently a client component so it will work properly wherever you use it, whether it's in a sidebar or maybe later in some other component that does not have `use client` at the top. All right.
**Number Four: This one is about thinking that if you wrap a server component, like `Product` here, within a client component, that it automatically becomes a client component as well.**
So, the typical example here is with the Context API. So, I created a context provider here, and it's actually just a normal React component. It's typically just called like this, and it's using `useState` here to keep track of the active theme. And because I'm using `useState` here, I need to make this a client component, and so this component here is a client component.
And the way it works with the Context API is that you need to wrap the part of your app that needs access to that theme with that context provider. Now, we could do it here in the layout or in the root, but let's actually try wrapping this `Product` here, which we know is a server component, and I'm using `async` here and fetching data here in the body. It's a server component. Now, I'm going to wrap this with that `ThemeContext` provider component, which is a client component, right?
So, here now I've wrapped that with this client component, right? It's a client component. It's wrapping this server component. It's using `async` and then directly in their effect. So, you may think, "Hey, that's not possible because this is a client component, and now this `Product` here should automatically become a client component as well, right?" And actually, if we go back now, everything actually seems to work. If I open up the console, I don't get any errors.
So, actually, a server component can stay a server component. A `Product` component is still a server component despite being wrapped here by this client component. And this is confusing to a lot of people, and maybe that's because we just saw that if you import a server component into another file with `use client` at the top, it automatically becomes a client component as well. And that's true, but it works differently here if you just render the server component within a client component, and that client component just takes that server component in it, which it's getting here as `children`, and just passing along the `children` here. That component will stay a server component, and so `Product` here will stay a server component because we're using the children pattern here, right?
So, if I would import the `Product` component in here, right? So, if I would do, if for whatever reason I'm doing something like this, importing the `Product`, and for whatever reason—this is rarely what we want to do, of course, but just as an example—if I would do this, then yes, `Product` here would become a client component because I'm importing this component in a file with `use client`. It's about the import statements, not about how it's actually nested in terms of rendering, and that's a little bit tricky, right? So, whether something automatically becomes a client component, well, there's just going to be a compiler looking at your import statement, and it's not going to look at how everything is nested here in the render tree, you could say, right?
So, here, if I undo this, this client component is just taking the `children`, just passing it along, and therefore the `children` can stay a server component, no problem. And it makes sense when you think about it because very often these context providers, they wrap the entire app. So, here I'm doing it around the `Product` component, which is a server component, but in practice, we actually may even want to wrap our entire app with this. Typically, you would have something like this in the root layout. So, the entire app is now being wrapped with this context provider. So, we actually have another `children` here. These are basically just the pages of our app, but those pages, remember, a page component is actually also a server component by default, and this `Home` component is a server component. And now you may think, "Oh, I'm wrapping it inside this client component, this provider here, so the page now becomes a client component, right?" And no, that's not true. It can stay a server component because this context provider component just takes everything that it gets as `children` here and just passes that along, and that can stay a server component.
Now, it makes sense when you think about it because it would be kind of strange that if you use the Context API like this, which is very typical, that you would automatically, you know, get rid of all the server components in your whole app and you lose all those benefits. Well, that's not how it works, right? So, if I undo this and go back to this, so if you use that children pattern for the server component, it can stay a server component despite being rendered within a client component, right? So, you can interweave them, as they call it. So, you can have a server component within a client component, right?
So, just to drive home that point, because I see so many people struggle with that: my `FavoriteButton` here is a client component. So, if I write it like this, and here we have `Product` here is a server component. If I for whatever reason want to put it in there, I want this to stay a server component. I can do that, but I need to open up this client component here. I need to accept all that as a prop or `children`, right? Children pattern. And then I can just pass it along here. If I do it like this, for whatever reason, well, it's going to look a little bit different now, but this still works because React can keep that a server component. And I can actually just create a prop called `example` here, right? I can pass a component, and then here we can accept that as a prop `example`. I can just output it right there. So, if I do it like this, we get the exact same result, right? So, you can pass a server component as a prop to a client component, and it can stay a server component that way, right? So, you can interweave them, right? So, `Product` is a server component, is only running on the server, but it's being rendered within a client component. How React or Next.js is able to do that behind the scenes for us is not that important to know from our developer perspective; it just works, right? So, a little bit tricky here, so let me undo this.
Now, if you really want to master React and Next.js, by the way, I do recommend that you go through my React and Next.js course. We start from absolute scratch, and by the end, we're building senior-level, cutting-edge Next.js projects. So, I highly recommend you check that out as well.
**Number Five: Trying to use state management on the server side.**
So, if we take a look at that context provider one more time, I'm using `useState` here. You may say, "Ah, that's why we're using `use client` here." But even if I remove `useState` here, and let me just make this like a string, so now I'm not using a React Hook, and you may think, "Oh, I can leave off `use client` therefore." And the answer actually is no. We will get a warning here because I'm trying to use `createContext`, and you can only use the Context API on the client side, right?
So, if you take a look at this overview again, here we have our client side, here we have the server side. So, all of those state management solutions, like the Context API or Zustand, all of this only works on the client side. So, if I'm trying to use `createContext`, I need to add `use client` here. And if I want to consume that context somewhere, I will have to use a React Hook, `useContext`. This will also not work here in a server component because this is a React Hook. React Hooks only work in client components, right?
So, all of that state management, all of the Context API, all of these other ones, all of that only works on the client side. And it makes a lot of sense actually when you think about it. The goal of state management is essentially to keep track of something. You cannot keep track of that on the server. So, for the server, it's always about a request-response cycle. So, there's an incoming request, maybe it's for a server component or some of these other ones that we'll take a look at in a second. Next.js takes care of that for us. There is some request; the server does something with that request and then returns a response. After that, the server does not keep track of anything from that request, so it kind of forgets about it.
Now, the browser can keep track of that, right? Because the browser will be there for the entire duration that the user is interacting with the website, right? So, for the typical state management solutions like these three, you can only use that on the client.
**Number Six: Using the `use server` directive to make a component a server component.**
So, this is what I see sometimes. Here we have our `Product` component, and here we're trying to use `async`. We can fetch directly like this, and perhaps we know that this can only work in a server component. So, what some people do is they try to make it a server component, so they do something like `use server` because that's the opposite of `use client`, right? `use client` is how you make something a client component, and then they think `use server` is how I can make it a server component.
But you don't need to do that because everything by default here in the Next.js App Router is already a server component. And it doesn't matter how I structure my folder structure here; I like to have this `components` folder outside the `app` folder. That doesn't matter; everything in there is still by default a server component. So, I don't need to do this. In fact, what you're actually doing is you're creating a so-called server action, which we'll talk about in a second. Those are two different things, right? So, a server action actually exposes a POST endpoint on your server side, so this can actually become a security issue if you do it like this.
Now, maybe your intention was that this should always stay a server component; it should never become a client component. For example, you may accidentally import it into a file with `use client`. In that case, it will become a client component, right? It will become a client component as well. But because you're using `async`, which is not possible in a client component, you will get a warning anyway. When you check your console, you will see that warning. Now, you may not see that warning immediately. In the past, I believe Next.js would pop up like an error message in here. These days, it's like a silent error that you get in the console.
So, if you really want to make sure that this does not get imported into a client component, there is another utility sort of that you can use. So, this is from the Next.js documentation as well. So, there is a package called `server-only`. So, you may want to install that, and you only have to write this one line. And now, if you would import this into a client component, let's actually make the page a client component. So, if I do this, now I get this error here, right? So, "You're importing a component, need `server-only`." So, now I can fix that mistake. You don't need to use this; this is not that common for a server component, I would say, right? So, typically, you would just leave it like this. We'll talk more about server actions and also that `server-only` utility a little bit later as well. All right.
**Number Seven: Accidentally leaking sensitive data when you pass data from a server component to a client component.**
Let's say you're getting your user data from your database, and let's say for the sake of the example here, there's also a password there. Now, you may pass that data as a prop to some other component, and if that component turns out to be a client component—so this `FavoriteButton` is a client component—it's getting that as a prop, and let's say we're just logging it. Well, now what happens is that that data is going to be visible on the client. So, here in the console, we now see this object here with the password, and this may, of course, not be the intention. You don't want to leak the user's password, of course, to the client.
So, Next.js abstracts away that network boundary for you. So, when you pass something as a prop from a server component to a client component, it will cross that network boundary; it's going to be visible on the client side. So, beware of that when you're dealing with sensitive data. You want to have some security best practices, and so you want to hash the passwords, and you want to query the database so that you're actually not getting back the password in the first place. You may actually want to have a separate data access layer. Those are topics for another day, but it's good to be aware that you can pass data across the network boundary with server and client components like this. All right, don't worry, almost finished with these gnarly server and client components; there's a couple more.
**Number Eight: Thinking that client components only run in the client.**
So, this one honestly is very tricky. If I log something in a server component, I could take the `Home` or let's actually take the `Product` here. So, if I just `console.log` something here: "Hello from Product component." So, this is a server component, right? So, if I `console.log` something, your intuition may be to actually open up the console here in the browser. So, if I refresh here, you may expect to see that here. We actually don't see that, and that's because this is a server component. The component does not run in the browser, meaning all of the statements in this function body here, they do not run in the browser. These statements only run on the server.
Now, the result of that render, a so-called RSC payload (very fancy word), is sent to the client. That's why we still see, well, this product title, right? This is coming from that server component, but the actual statements in order to produce that result only run on the server. And so, a server component is easy; it only runs on the server. So, if you want to see `console.log` here, you need to open up the terminal actually because this is where you log things on the server. And so, indeed, here I can see my logs here, right? Right. So, I bet that every Next.js developer actually made this mistake. Now, this is still easy to understand, right?
All right, so now let me make it a little bit trickier. So, this is a client component. So, this `FavoriteButton`, I'm now also going to run a `console.log` here. So, I'm going to say, "Hello from Favorite Button component." I'm going to close this. Okay, so now I'm logging here, and now you think, "Ah, `use client`, this is a client component, so now these statements in the component function body, they run in the browser." And that is correct, right? They need to run in the browser because we're trying to hook into that event, click event, right? That happens in the browser. So, this code and this changes the state, right? So, this code needs to run in the browser. So, if I inspect that, and if I do `console.log`, let me refresh here. Indeed, we see the log right here, right? So, this works as expected.
Now, the tricky part here is now if I open up my terminal here, where we see the logs on the server side, what do we see here? We see "Hello from Favorite Button component," which is a client component. How is this possible? Why do we see the log here also here in the terminal for the server side? Well, that is because Next.js by default will try to pre-render the entire page to HTML. So, everything that you see on the page, and the page is just a component, just a React component. So, in this case, it's this `Home` component. Next.js notices this is a page component because it's in a file called `page.tsx`, which is a special file. Next.js will automatically take a look at the default component in there, and it will pre-render that to HTML at static site generation (SSG). You probably heard of these buzzwords. So, it can already generate HTML out of this. So then, when a visitor comes to our website, we don't have to render anything else; the HTML is already waiting for them. It can be placed on a CDN, can even be closer to the user, so it's very fast.
Now, in order to generate that HTML, it has to run all of those components. It has to render all of those components, meaning all the statements inside the function body have to run, right? You have to get a result here, so you have to run the statements in here. So, it will try to create HTML out of this. So, it's going to run the `Product` components. All the statements in here, they're going to run. And then here we have a client component, and it will actually also run the statements in here, and all of that is being run on the server side, right? So, that's not running within a browser; that's running on the server side, right? So, it's just running that once, and therefore, initially, one time, this statement is also run on the server side. That's why you also see this log here, but only once, right?
So, as you interact with this component, now you can see the state changes, which will trigger a re-render of this component. Now, all of these logs are only being displayed here in the browser, not on the server side, right? So, the server side is just once, and then it's being shipped to the client where it will re-render over and over again as we interact with the component. And so, actually, really tricky, right?
So, Next.js will take all of this here on the page, will try to create HTML out of that. It will send the HTML to the user, and the user is just going to get a bunch of HTML. But remember, we need some JavaScript logic to make this `FavoriteButton` actually work. So, React will take that HTML and it will hydrate it, as it's called. So, it will go over all the HTML. It has received instructions that that button should be hydrated, basically needs to add that event handler. It needs to add the actual JavaScript logic for that specific button to make that functionality work, right? So, once it's hydrated, we can actually interact with it, right? So, a little bit complicated, but the takeaway is: server components only run on the server; client components in the client, but also once on the server side, right? So, hope you're still with me, and we're almost finished here, so hang in there.
**Number Nine: Trying to use the `window` object or `localStorage` or any kind of browser API in the wrong way.**
So, we just saw that a server component only runs on the server, right? Easy. But a client component, well, it will run in the client, right? The browser. It will run in the browser, but also once on the server side because as an optimization, Next.js will create HTML, pre-render HTML out of this. So then, when a user comes to the page, nothing has to be rendered; the HTML is already ready to go. So, it can be served from a CDN; it's just a static HTML file, right? So, the user will just receive the HTML here, right? So, the user's browser will receive all that HTML.
Now, of course, some of the parts in that HTML need to be interactive, right? So, that button, for example, when I click on it, something has to happen; the icon needs to be changed. And that's what we're doing here; we're changing the icon here when I actually click it, right? So, there is some interactivity here, so there needs to be some code running. And so, with the HTML also come some instructions for what's called hydration. There will be an instruction for that button here, and so React can go to that button, can attach the event handlers—in this one, in this case, only this one—and so then the user can actually interact with it. Usually, it's pretty fast, and then the user can actually interact with it. If the user clicks on it before hydration, it will not work, right? But usually, it's pretty fast, and that's what people mean with hydration, a very fancy word, and that's why the client component will run on the server side.
Now, the biggest problem with this usually is actually when you try to use a browser API, so something that's only available in the browser, like for example, anything with the `window` object, right? So, I can use `window.localStorage` or just `localStorage`. You don't have to write `window` in front of it, but it's still part of the `window` object, right? `localStorage` is only available in the client; this is not available on the server. And you think, "Ah, `use client`, right? So, this is a client component, so this will run in the browser, right? In the client, so `localStorage` is there, so this should not be a problem, right?"
So, maybe what we want to do is that when the user clicks on that, we want to persist that in `localStorage`. So then, the next time the user comes back, we can check `localStorage`, and we can check if the user has already favored this item before. And so, we could do something like `localStorage.getItem`, maybe we store it under the `isFavorite` key. Then we know if the user has favored it before, and then based on that here, we can say "yes" or "no," right? Just a silly example. The point here is we're trying to access `localStorage`, and as you can see, everything here looks all right; we don't get any errors. So, this looks all right.
Now, what happens if I open up the terminal here for the server side? If I open up my terminal, we actually have some issues. So, here it says something about `localStorage` is not defined. Well, that is strange because this is a client component, so this statement here where we actually try to access `localStorage` should only run on the client, right? And that's actually not true, as we just discussed, right? So, a client component is also run on the server once. What that means is that the statements in this function body are run on the server, and here we are trying to access `localStorage`. This does not exist in that server-side Node.js environment. So, therefore, here when we open up our terminal, when you try to access `localStorage` or actually anything on that `window` object, right, it will not be available in that server-side environment, and so you're going to get this issue here.
So, to solve that, there are a couple of solutions. I could simply rewrite it a little bit. So, here we could simply just check that when we run these statements in here, we can check if the `window` is defined or not. If it's not defined, we're just simply not going to access anything on the `window` object or `localStorage`, right? So, only when it is actually defined now will we actually use this variable `localStorage`. And now, if I open this up, you can see it compiles without error now, right? So, you can solve it with a `typeof window` check.
Another solution could actually just be using `useEffect`. So, React will not run `useEffect` on the server, and so you can access the `window` object in `useEffect` because remember, `useEffect` runs after, right? So, everything here will run. All the statements in here will run, and React will render this, and after that render, `useEffect` is run. Now, when React pre-renders this to HTML, it will run all of this and then just stop. Once it has the HTML, it's good to go; it's not going to run `useEffect` on the server side.
Now, you can actually also just leave it like what we just had, so just without `typeof` or `useEffect`, just leave it directly in here. This will work if it would only run on the client. So, if you want a client component to never run on the server side, it will not be part of that pre-render. If it should only be rendered on the client side, what you can actually do is use a dynamic import. So, here in Next.js, they give you this `dynamic` here, and here I can say `server-side render` should be `false`. So then, here I should also change this. Now, this component will not run on the server side ever. It will just be imported on the client side and will only run on the client side. So then, we can just access `localStorage` because it's only running in the browser anyway.
All right, so keep in mind, Next.js will render client components on the server side as well, part of that initial pre-render, right? So, a little bit tricky. It's okay if you're confused; these client and server components, it takes some time to get used to them.
Now, don't worry, almost finished with server and client components. Let's quickly finish it up here.
**Number Ten: Getting hydration errors.**
So, this has to do with what we just discussed. Remember, Next.js will try to pre-render the page component on the server side. This is basically like an optimization. Now, we just saw that if you try using a browser API like `localStorage`, since this is running on the server, you will get an error here with `localStorage` because this does not exist on the server side. This is something that exists in the browser, but not on the server side. So, if you run this statement here, this line of code on the server, you will get that issue, right? So, if I save here, and if I refresh here, if I open up my terminal, you will see we get that issue, and `localStorage` is not defined on the server side. All right.
So, we covered basically three solutions for that. We can pick any of those solutions. So, one of the solutions is to simply check if `window` is defined or not, right? So, here I create a variable, and then in here, we're actually going to access the `window` object or `localStorage`. You can actually write it without `window`, right? So now, only if `window` is actually defined, meaning we are running in the browser, will we actually try to access it. So, when this runs on the server side here, it will run this code. It will actually run `useState` here, will actually initialize it with `false`, so `isFavorite` will be `false`. And then the server side creates this variable, but then here, `typeof window` will be `undefined`, right? So, here it will not go in here on the server side. It will just continue with "has been favored before," right? So, it will just continue here without that variable having a value. So, you have favored this before. So, on the server side, this will be falsy, right?
So, here on the server side, what eventually will happen here is we get HTML with the string "no," right? Because on the server side, this is going to be `false`; it's actually going to be `undefined`, this variable, and so we're going to get "no" here in the ternary. So, this page on the server, what it will result in is HTML. So, if we actually just think about it here, so the pre-rendered HTML, they call it pre-rendering. So, here we're still going to have this `H1`, right? "My Store," right? That's going to be in that HTML. Then whatever this `Product` component produces, that's going to be some product title. And then in the `FavoriteButton`, we will have a button, but we're going to have that, uh, let's see, we have, we will have this "no" here. That's the pre-rendered HTML.
So now, that HTML will be sent to the client, right? And on the client, since this is a client component, it will be hydrated, as it's called, because here we will need some logic, right? So, this needs to add event listeners to make that work. This HTML will be hydrated. Only this particular piece, essentially this `FavoriteButton` component, will initially not work. And so, initially, it's just some static HTML, and then React will hydrate it so that you can actually interact with it. It will actually work, right? Meaning the icon will actually be changed. So, when it's hydrated, this component runs again, right? So, now it's going to run on the client side, so it will run here again, and now `window` is not `undefined`. So, now we actually will go in here, and if this item in `localStorage` is falsy, what we will get is "no" here as well, right? So, let's say after hydration, we get the exact same HTML essentially, right? There's no change.
So, the problem here will start when... scroll up a little bit. So, here, what if there is actually something in `localStorage`, and we actually see that the user has favored it before? So, now it will give us `true`, let's say. So, now this variable will be `true`.
So if I go down here, it will still give us the same pre-rendered HTML from the server, right? Because on the server side, it will always be undefined, right? It, it will always give us no on the server side. But in here, now, in the browser, it will now give us true. So here in the browser, you have favorited this before. Now it's going to give us yes in this turn, right? So now what we will get after hydration is, we will get yes here. Now React will give us a problem because it will say, "Hey, the HTML in the browser does not match up with what was rendered on the server side."
So now if I, uh, save here, actually, and I go back to our project here, if I refresh, you can see I'm getting an error here. Text content, right here, does not match server-rendered HTML, right? So this is that hydration error that you get sometimes. On the server, it got no. On the client, it got yes. And so that's something you'll run into sometimes. Now, it's basically a warning for us as developers, so we can double-check if we made a mistake. In this case, we didn't really make a mistake. So you may want to use `suppressHydrationWarning` if you really know what you're doing and you know it's not a problem. This is actually maybe something that there's no way around. You can just use `suppressHydrationWarning`, and because sometimes there's no way around it. Sometimes, for example, also if you use, for example, `new Date()`. You use `new Date()`. Well, when this runs on the server, it's going to give me some date. And then later, when it gets hydrated again on the client, this runs again. Well, that, that's going to be a bit later. So necessarily, it's going to be different. It will give you a hydration warning. Well, that's, there's no way around that. So you may want to use, well, you may just want to suppress that warning. You don't get a warning anymore.
You will also get that warning, by the way, when you're using incorrect HTML structure. So, for example, if you're using a `div` in a `paragraph`, even if I comment this out, yeah, so here I still get that issue here. So this will also give you a hydration error. And that's because the browser, when it sees incorrect HTML structure, it may actually try restructuring it automatically for you. And that's not going to happen on the server side. So then you also have that mismatch. And I would say incorrect HTML structure is actually pretty common. Now, this is honestly the most complex mistake that I've seen. So don't worry if you don't fully get it. I, I think even experienced developers are going to struggle with this.
All right, let's wrap it up here with number 11: incorrectly dealing with third-party components. Very often, actually, you're going to use a third-party component. Let's say you installed some npm library, React Amazing Carousel. So you import the component, and then you want to use the component. Now, maybe, maybe this component is using a React hook or is hooking into events, `onClick`, `onChange`, just like we did here, actually, in our favorite button, our own component here. Here we were using `onClick`, and here I'm using a React hook. So that is only possible if it's a client component. So what we did here is, in our own component file, we can just add `use client`.
Now, what you're going to see with many third-party components is they're going to give you a component like this, and they're using React hooks, but they didn't add `use client` to the file yet, right? Very simple, but they, they just didn't do it yet. Or maybe they have some good reason for that. So that means if you try using that in a Next.js application, this component is using a React hook or event handlers without `use client`, and so you're going to get an error. Now, with your own component, you can just open up the file, right? So `favoriteButton`, it's using that as well. I can just open it up, edit here. That's not what you do with a third-party component. I cannot just open up, like, the, the source code basically and add `use client`, right? So here, what Next.js does, actually recommends that you just create, like, a wrapper component. Here, we actually don't need to create a separate component. We can actually just export that as the default from this file because it's just based on the file, actually. So the only thing we have to do here is make sure we are importing that into a file with `use client`, and then we can immediately export it as the default. And then if you want to use that carousel, you import it from this file, right? So now I would import it like this. So now, since this component is being imported into a file with `use client`, it will automatically become a client component as well. So if it's using React hooks or event handlers, you're not going to get an issue anymore. So this is something you have to do with third-party components sometimes. So that's if they're using React hooks or event handlers.
Now, what if they're using a browser API? We just saw that there are some additional issues, essentially. In that case, because this will run on the server side first, so if that carousel, if it's using a browser API like `localStorage`, `use client` may not be enough because even client components are rendered on the server, right? And I cannot just open up the source code of this component and change that. So how do you deal with that? Well, in that case, you're probably going to have to do a dynamic import of that third-party component. So that's basically the only way to make sure it only runs on the client, right? So that's how you can deal with third-party components properly in Next.js.
So that's pretty much everything I had to say for now about server and client components. It's a new paradigm, so there's a bit of a learning curve. So don't worry if you're a little bit confused. I have a complete React Next.js course in case you really want to master the latest React Next.js. Highly recommend you check that one out.
All right, so next, I actually want to talk about data fetching and data mutation. So let's go back to our diagram one more time. Let's take a look. So here we have our overview. Now, your traditional data fetching, what I mean by that is just `GET` requests. And in the latest Next.js, most of these, you want to do in your server component. And then there are also so-called data mutations. So these are basically your traditional `POST`, `PUT`, and `DELETE` requests. Most of these are now going to happen in your server actions. So what about these route handlers or or API routes? What is the use case for them in the latest Next.js? It's mostly not going to be these two, right? So I've seen some people, they think you still have to create a separate API route. Well, that's not the case. You can just do that with server actions, actually much easier. You can just write a function instead of spinning up a whole API route. And let me actually write this like this. Let me make this a little bit bigger. So a common use case for these route handlers is actually webhooks, right? So maybe you're using Stripe, and there's a payment. Stripe will send a request to your server so that you can update your database. Well, you can have an API endpoint for those webhooks. That's what you typically use this for. But for your traditional data fetching or data mutations, you typically don't spin up a separate API route anymore, right?
So number 12 is actually the mistake of getting data by using these route handlers. So, for example, traditionally, if you want to get data from your database, for example, maybe you would create an API endpoint, and then in there, you would fetch your data. Let's say you're using Prisma as an ORM. So here you would allow a request, and then you would get all the products from your database with Prisma as your ORM, let's say, and then you would return that to the front end as JSON data, right? And then if you actually want to hit that API endpoint, maybe you would use `fetch`, right? And then you would do something like `/api/products` to hit that API endpoint, and then you would get back a result, and then here finally you would have your products. Now, this technically works, right? So you could technically from your server component make a `fetch` call to your API endpoint, and then it will return there. But that's not necessary because a server component is also already running on the server side. So what you're doing in a route handler, you can do in the server component directly. You don't need to spin up a separate API endpoint for that, right? So what I'm doing here, this call to my database, I can do that directly in here, right? So I can replace all of that, and I can just do it directly in the server component. I can delete this API endpoint altogether. I'm actually going to delete everything here, and I can just do it directly here in the server component, right? So no need to use a route handler for getting data. You can do getting data directly in the server component. Here I'm using an ORM, I'm not using the `fetch` API, but we saw before in this product component, which is also a server component, I'm actually using the `fetch` API here to get data from some external API. And that's also possible. And I could do the fetching in this component. I could also do it in the page component here, and then pass down the data to that component. Some people like to fetch the data in the page component because then you have a nice overview of all the data that is being used on the page. But it's also nice to fetch the data in the component where you're actually going to use it. So in this example, we're doing it directly in the server component where it's actually used, and we don't have to spin up our own route handler to do that. We can do these `GET` calls directly in server components.
Mistake number 13: thinking that it's a problem when you get the same data in multiple places. So let me show you what I mean by that. So here we're making a `fetch` call in our product component. So here we're getting some product information. We get the product information here, and the only thing we're actually using here is the title. So I may actually also should have called this component `ProductTitle` or something like that. Now, let's say we have another component for the price, right? So here I added another component here. If I open this up, we can see it's also a server component, `async`. And here we want to output the price of that same product, right? So here we're outputting its title, and here I want to output its price. I need to get the same data. So I'm making the exact same `fetch` call in this component as I'm doing here. And here I'm then outputting the price. And so now I also have the price of this product here on the page. So you may think, "Uh oh, what we need to do here is, we need to pull it out of these components." And you know, we have to sort of lift it up, right? Just like with `useState` when you have to lift up state. Here we have to lift up the data fetching, perhaps a common ancestor here, so that we only make the call in here, perhaps, and then we can just pass the data down with props. And then here I can remove it from here as well. So now we would only be fetching it in this page. Now, actually, you don't need to do this. And this would also be a problem because, well, it's still a bit easy here, but what if this price was nested in some other component? You would have to do a lot of prop drilling to get the product all the way down there. And remember, you cannot use state management on the server side, right? To solve that prop drilling, we don't even have to do all of that. We can actually just fetch directly in the place where you need the data. So here in `Product`, if I need it here, I can just make the `fetch` call. And here in `Price`, I can make it `fetch` call. Now, at first glance, this looks duplicative. It's like we're making two `fetch` calls for the same product data. This does not look optimal. Now, I have some good news. This is perfectly fine because there is caching for you behind the scenes, actually, by both React as well as Next.js. So what React actually does is, if you have a `fetch` call like this, it will not run twice in the same render pass. Let's say the page is being rendered. So React can actually already see that you make the same `fetch` call, so it will only make that `fetch` call once, right? So this will only run once, and the result of one of those `fetch` calls will simply be used for the other one as well in the same render pass, as it's called. Now, in addition to that, there is actually also the Next.js data cache. So Next.js will actually store the result of this `fetch` call in a so-called data cache. And that may actually be the most powerful cache in Next.js because that will persist even across deployment.
Now, here I'm making the `fetch` call like this, but often in practice, you actually have utility functions for this. So you may actually create a utility function called `getProduct` in which you do the actual `fetch` call, and then you would use that one to get the actual data in here, right? So you would do this in here, and then in here you would do, right? So I need to import it here and here as well. But this doesn't change anything, right? So here now I'm using that utility function instead of the `fetch` directly in here, but this doesn't change anything, right? But you do it through some function or directly use the `fetch` API in there, that doesn't matter. It works the exact same. So this is perfectly fine as well. So there's lots of caching done for you behind the scenes. I'll have a separate video on that. And one of the benefits of that is that you can fetch data directly in the place where you need it. Now, that is the case automatically for you if you're using the `fetch` API. So React and Next.js will automatically cache `fetch` API calls. Now, if you're using an ORM, that is not the case. The idea being that those ORMs may have their own caching behavior. So if you want to have that for those ORMs as well, you may want to use the `cache` function from React, which will deduplicate it for every server request. But that's temporary, right? It's just for one server request coming in. The server side will do something with that request, right? Maybe multiple server components are trying to get that data. So React will will cache that for all of them, will only run at once, and then the response is sent, and then it's over. It doesn't remember anything. It just removes the cache. But there's also that data cache from Next.js. So at the time of recording, that's still unstable, but you may want to use the unstable `cache` function. In that case, it will be put in an Next.js data cache, a very powerful cache that persists even across deployment. So then your ORM calls are actually also cached, right? This is a bit of an advanced topic, and I'll have a separate video on this. And actually, here I'm getting all the products from the database. So a more appropriate name would be `getProducts`.
Mistake number 14: getting waterfalls when you're fetching data. So I modified the example again a little bit. So now let's say we're actually fetching the data, all of the data here in the homepage component, instead of fetching the data in the product component. I sort of lifted it up to the page component here, just for demonstration purposes, right? So I created a utility function for this `getProduct`. This is the exact same as I was doing in the product component before. But very often on the page, we don't have just one component that needs data. We have, well, sometimes a lot of components that need different types of data. For example, here we may also have ratings, we may have testimonials, write comments, all sorts of different data could be on the page. And here is, for example, where I'm making another `GET` request, `getRatings`, and that is some other URL, and I'm getting the data here, and then I'm passing that to some component to actually render something. And sometimes these components are nested inside of each other. But for this example, it's easier if I show you it like this. So the problem is, you may actually already see it. Before we can start fetching the data for ratings, we have to wait until the previous one is finished, right? `await`. So with `async/await`, the way it works is, this line will not run until this has been completed, right? So the way it works with sequential data fetching is that you have your first request here, it would be that `getProduct`, and then only after that, once that is finished, can we start making the next request, right? In this case, `getRatings`, right? And if we had another one, it would have to wait until that one was finished. If they each take two seconds, in total, this would take six seconds, 2+2+2=6 seconds in total. And sometimes there's no way around it, right? If this data fetch depends on some data from here, right? You need some data from here in order to make this fetch call. There is no way around that. Now, here that's not the case, right? So these are two independent data fetches, right? So this is also called a waterfall effect, right? So it looks a bit like a waterfall. Now, I wish I could show you that in the network tab here in the browser, but this is actually a server component, so I don't see that in the network tab in the browser, right? So I can show you that that way. So ideally, if these fetch calls do not depend on each other, they are independent, ideally we can just start making them all at the same time, right? At at the beginning, right? So we don't want to have that waterfall effect. We don't want to fetch them sequentially. We want to fetch them in parallel, right? So all at the same time. So now, if they all take two seconds, well, they all start at the same time, so after two seconds, they are also all finished. So now the total time will be two seconds, right? So if you can try doing parallel data fetching.
So how would we do that here? So what you could do is actually use `Promise.all`, right? So instead, we could do this, right? So `Promise.all`, and then here an array of all the promises. Since these are async functions, they always return a promise, so I just have to call them, not await them, in here. This will result in an array of promises, and those promises will all be run at the same time, and then the results will be in here. Now, one of the downsides with `Promise.all` is that, for example, if `getProduct` fails or rejects, this promise rejects, it will reject for the entire array here. So `getRatings` would also be affected by that. So some people will argue that there is another method here, `Promise.allSettled`. It is actually a little bit better because this one will always resolve, even if one of the individual ones reject, the other ones can still resolve, and those data can then still be used, right? So I think this is good to know when you're doing data fetching.
Now, by the way, that waterfall with data fetching can also occur if you structure it a little bit differently. So maybe you're not doing all of the data fetching directly in the page component here. Maybe you're doing that in the individual components. So here in `Product`, maybe I'm fetching some data here, right? Just a utility function, it's getting some data, and we need to wait for that. And then here I have the other component of `Ratings`, which may also do some data fetching, right? `getRatings`, just like what we saw before. But now this is nested inside another component that's also doing data fetching. So that fetch in there will not start until this one has been completed, right? So now you also get a waterfall effect. I'll actually have a separate video on this because it's quite tricky. So you may want to restructure things. You want to be aware of how you're doing the data fetching. So in this case, to avoid the waterfall, you may need to trigger that data fetch a bit higher. And then if it's using `fetch`, for example, it will be cached the first time. So then later, you you can just fetch it wherever you want. That's a little bit complex. I'll have a separate video on that.
All right, so what we just discussed was all for getting the data. Now let's talk a little bit about those other actions you typically want to do in your data. You want to add new data, update data, delete data. Fancy word for these is called data mutations. So mistake number 15 is actually that people will try to submit data to a server component or a route handler. So first of all, you cannot submit data to a server component. So just to show you what I mean by that. So here we have our page, and I added a link here, right? So we still have our page here, just like before, but now I added a link. Also, a component in Next.js has certain benefits, right? So on this page here, we are getting data, right? So we are getting data. So now what I did is I created another page where we can submit data. So we'll have a form, and I added this link here so we can go to that page, right? So the route for that page is `/products`. So you can see here I created a `products` folder and another `page.tsx` file there. So on this page, let's actually go there and see what we have there. Here I have "All Products", and what I want to do here is I want to be able to submit something on this page. So what do we have on this page here? We have "All Products", and then here we are actually also getting data. So I actually added a database. In this case, we're going to use Prisma to get all the products from our own database, right? So we're still getting data here as well, and then mapping over that here. But since there is nothing in the database here, we don't see anything here. But then what we can also do on this page, we have a form here, and we can actually submit data, right? And then ultimately, I also have this link here to go back to the homepage. So that's this page. Now, here when you have a form with just one input, and I want to add a product to my database, right? So if I add just some gibberish, "test", if I click on this, typically what you want to do is you want to submit it to your backend. And so you would say "Add Product", and when the form gets submitted, you would do something like `onSubmit`, and then you would do maybe a `POST`, right? So something like `/api/products`, `method: POST`, right? Something, something like this, you would have your body, and you would try to submit it, right? So in Next.js, some people still do it that way. And sometimes they want to kind of submit it to their page component or something like that. That's not possible, right? So you're not going to get props from your, that's not, it's just not going to work like that. Sometimes they will try to submit it to a route handler, just like what we saw before. So you would have `/api/products`, you would send the data to your route handler, and then your route handler would add it to the database. You don't need to create a separate route handler because these days we have server actions. So with these server actions, we don't have to create a whole API endpoint and then use `fetch` and then awkwardly make sure we have the correct URL. We can remove all of this. Instead of using `onSubmit`, we can use the `action` attribute in Next.js. And here I can just specify a, well, basically a normal function, and I call that function `addProduct`. And there's just some function here. This is a so-called server action. Now, I like to collect all of them here in an `actions` folder where I can have all of them in one place. Here is where I defined that function. So here I have a folder for all my so-called actions. I like to create a separate folder and then just one file, usually, with all my server actions. So I have all of them in one place. And this is basically just a normal JavaScript function. In here is where I'm going to add something to my database, right? So here I have the `use server` directive. This makes this function a so-called server action, right? So you don't use `use server` to make something a server component, you use that `use server` directive to make a function a server action. So now, what I want to do when I submit data or when I update data or delete data, usually you want to use these functions, right? So here, if I specify a function for a form, and I submit the form, Next.js will actually give the data from that form as an input here to that function, which I can then use to update my database. And I don't have to create a whole API endpoint. I just work with normal functions like you would normally already do in your app. And Next.js, once again, is abstracting away that network boundary. So Next.js will make sure the data actually goes to the server and is actually, well, you could say submitted to that server action. And it's a function that only runs on the server. And Next.js will also make sure that when the form is submitted, actually submitted to that function running on the server, and so we get that data right here, and so we can update a database.
Prisma allows me to inspect the database. So right now, you can see in the `Product` table here, you can see I have nothing yet. That's also why here we don't see a list, right? So if I close this again, if I go to `products` here, this page here, so here we are getting all the data as well, and we're mapping that over here, but you can see there's nothing in the database yet, so we don't see anything. Let's actually first try to actually get the data from this form in my browser here to the server side, right? So let me actually show you how this would work. So I'm going to submit now. And since I specified that function for `action` here, it will get the form data, just one input here, which I call `title`. It will get that right here, and I can immediately insert that here in the database. And so let me show you how that would work. If I just say "test" here, if I click on "Add Product", and now I clicked here, we don't see anything here, but now if I go back to my database and I refresh here, you can see there's a new row here with `title` is "test", right? So pretty cool, I think, because we don't have to create a whole separate API endpoint. Next.js abstracts away that whole network boundary. Next.js will make sure that the data here from the form in my browser is actually submitted to the server. A lot of people talk about server components, but in my view, it's actually these server actions that are the biggest innovation. Also, because that works, that the way I specified this here, this form would work even without JavaScript enabled. So the fancy word is progressive enhancement. And it's also integrated with some powerful React hooks. So there is something called `useFormStatus`, so we can get the pending state, so we can show a loading indicator, for example. `useFormState` is typically used for an error state. `useOptimistic` for optimistic UIs. So very powerful, actually. And so this is what you want to use for those data mutations.
Now, by the way, I have a separate file here with `use server` at the top. You don't have to put these in a separate file. It's just what I like to do. Technically, you can also just specify it directly in this file here where I would use it, right? So I could actually just create a function in here and then use it here as a server action. And then actually, you do need to add `use server` here at the top, right? So I could also define it in the component like this. You will see this sometimes, but I think it looks a bit messy. I think it's cleaner to separate this out into a separate file with `use server` at the top.
So in the previous example, I added this one, "test", to the database, right? That's what we see here in the database. And so when I load the page now, on this page, we are getting everything from our database. I can do it directly here in the server component, and then I'm mapping over that here. That's now what I'm seeing here. Now, that's if I actually refresh here. Now, when you add something, you will not immediately see it here.
So this is mistake number 16: getting confused when the view does not update after a mutation. Right? If I do another "test" here, let's say "test two", if I add this product, I just click. And now if I inspect my database, you can see that I added "test two" to the database. Now you can see it's not displayed here in the list yet, okay? All right. Now, what happens if I go to the homepage and now I come back to that page again? You can see it's still not displayed here in the list. And that is actually because of caching, right? So Next.js will cache the result, the render result of a server component. And these pages are just server components. So once this page has been rendered, the result of that so-called RSC payload will be cached. So the next time you come back, actually, Next.js will not make another network call to render this page again. It will actually just use that server component payload from the previous time, which is actually a client-side cache in Next.js. I'll have a separate video on Next.js caching. It's so complicated. But the point is, it's still cached from the previous time. So if you want to make sure that you bust that cache when some data has been updated, you want to make sure the updated data is actually displayed. You can do that very easily in these server actions as well by using the `revalidatePath` function that Next.js gives you, right? So you can see it's also coming from `next/cache`. So you can bust that cache after I have updated the database and the data has been updated, I want to make sure that the view also gets updated. And when I come back, I can already see it. But let's actually try this out. So now, if I say "test three", if I now click here, and actually, you can see I don't even have to navigate, even when I stay on the same page, Next.js will make sure that the view is updated properly. I think that's actually a very nice user experience as well. Now, you do want to be a little bit careful here with the path that you specify here. So here I'm busting the cache for the `/products` route. So this page falls under that. And so everything that's cached here will be busted by that. If you're not careful and you would just do like something like this, you would essentially bust the cache for your entire app, which is not necessary because maybe we're only using the server action for one page, right? So here, for example, I would only want to bust this part of my app. So you can be a little bit more granular.
Mistake number 17: thinking that we can only use server actions in server components. Right? So here we are using this server action in a server component, right? So we have `products` page, which is a server component, and here we have the form, and here I'm using my server action. So this works perfectly fine. And in the server component, you can use the server action, of course, right? So this is also typically the examples that you'll see when people talk about server actions, they'll they'll take a form as an example. However, you can actually also use server actions in client components. It actually works perfectly fine. That's not a hack or something. You can use these wherever you want. Let's take a look. We have that all, we have the homepage. If we go back there, we have this favorite button here, right? If you go here, so here I have my homepage, I have a favorite button here, which is a client component. Now, if I want, when when the user clicks, I can invoke that server action as well, right? When I click, I want to run a function. And what do I want to do in that function? Well, I want to, let's say, invoke that server action, right? So it's called `addProduct`, and `addProduct`, I can just import it here. I could also pass it as a prop, and I'm just going to import it here. Now, what am I going to pass as an input? But so now I'm not using it in a form. So here we're not going to get form data. I can just actually just pass the title of the product, and then we can just use that directly in here, right? Let me actually type that as well. It's just going to be a string. So then here I can just say "test 10". So now when I click, I invoke the server action with some argument. That's what we will receive here in the server, on the server side, right? So Next.js will make sure that this argument is passed from the browser to the server, that we actually receive it here, and then we insert it into the database. So let's see if that works, right? If I click on the favorite button, it should be invoked. I'm going to click right now. I click. All right. So now let's go back to `products` here, and you can see we indeed see "test 10" here as well. And so you can invoke these server actions in client components as well. And we can even get a pending state as well by using `useTransition` actually and wrap it in a `startTransition`. But let me undo this for now.
Mistake number 18: forgetting to validate and protect server actions. All right, let's go back to the form example one more time. So here I have my form, and I added this action here, `addProduct`. So here we will get the form data, and we can then insert into our database. So this form is here in my browser, right? So I will type something here, "test 20", and when I submit here, Next.js will automatically make sure that this input here that I put in here is actually going to the server side to this server action here, and so we can insert it right here. And I can actually show you that in a network tab as well. And so here I have my network tab. Let's see what happens when I submit the form and the server action gets invoked. Let's see what happens. I'm going to click right now. All right. So you can see some things happened. We can ignore these, but you can see here there was a `fetch` call. Actually, if I click on the first one here, you can see there actually was a `fetch` call. I'm not making a `fetch` call myself, it's Next.js doing that for us behind the scenes automatically. And if I make it a little bit wider, you can see there was a `fetch` call, and it's actually using the URL on which that server action is being used. So you can see we have a URL here, `/products`. This is the location where the server action is being used, and it's a `POST` request, right? So we can see here that when you create a server action and you're using that, you're sort of exposing a `POST` API endpoint on the location where you actually use it, right? So you don't have to create your own API endpoint. Next.js does that sort of automatically for you when you create a server action. And so then when a server action is actually being invoked, Next.js will automatically make a `POST` request to that, uh, location. But this is very similar, and you may even say it's the same as exposing a `POST` API endpoint on your server. Which means what we get here as data could be anything, because other people may also send requests, right? Somebody else could simply make a `POST` request to this URL, right? And they could submit whatever data they want, right? And of course, you as a developer or other developers could invoke this server action in different places as well. So the data you get here, we are sort of assuming here it's going to be form data, but it is not necessarily true, right? We already saw that if I actually use it outside the form, it's not going to be form data, it's going to be something else. So before you even do anything with that input on your server side, you want to be very careful. You want to validate the incoming data, right? So you want to use something like Zod to validate that what you get here is actually form data. So you may even want to use the `unknown` type in TypeScript, which is a more accurate type in my view, because we don't really know what this is going to be. We, I mean, here if it's actually like this, it will indeed be form data, but if it gets invoked somewhere else, or somebody else makes a request to this, we don't know if it's going to be form data. It could be anything, actually. Since we don't know, the proper type is `unknown`. And therefore, we cannot just assume that we can do `get` on there and get a title. We want to get certainty on the shape of the data. Also for security reasons, we want to make sure that we're not, you know, doing anything with our database without exactly knowing what we're doing, because these server actions are very similar now to just a normal JavaScript function that you can just invoke, just like other functions, right? Next.js abstracts away that network boundary.
So the other mistake that people make here is simply forgetting to add the authentication check. And remember, a server action is essentially just a `POST` endpoint on your server, right? So other people could technically hook into that as well. So you want to make sure that when that function gets called, just like with your traditional API route handler, that you're actually making sure that the user is properly authenticated and also authorized, right? So here I'm using Kinde. I'm actually a brand ambassador for Kinde. It's a paid sponsorship, but I think it's a wonderful solution for authentication. And this would be an example of how you could do that check. And so you can check if the user is authenticated, and otherwise, you can redirect them. So make sure you also protect your server actions.
Mistake number 19: using the `use server` directive to make sure something runs only on the server. So we just learned that if you use the `use server` directive, you're creating a server action, right? So all the functions I would put in this file will become server actions, which means, as we just saw, you're essentially exposing an endpoint on your server that other people can hook into. And so you want to be a little bit careful with that. And that also means that if you have, let's say, a utility function, now let's say you have a `getProduct` as a utility function. You use it in some places, just as a utility. This is just a utility for for getting data. We're not updating data, so it doesn't need to be a server action. It's just nice to abstract away this logic here in a separate function, right? Very standard, very typical. Now, you may say, "Oh, I only want this to run on the server." So what some people are tempted to do is to say, `use server`. Well, that's not what you want to do because now this becomes a server action that's not intended. Like, let's say you don't want to use this in a client component, for example. So you say, "Well, it should only run on the server." What some people are tempted to do is to use the `use server` directive to make sure it only runs on the server. So it's true that this will only run on the server, and a server action runs only on the server, but it also exposes an endpoint, essentially. So that's not your intention here, right? If you want something to only run on the server, you may want to use that `server-only` utility. And you may need to install that. So `npm install server-only`. This is basically like a utility npm package that you can install and then just import like this. Now, if you try using that in a client component, you will get a warning, and you can fix that. And then you may also want to call the file in which you're using it, not just `utils`, but `server-utils`, where you can put all of your utility functions that should only run on the server. And it's the same with server components, right? So if you have a component, and it should never become a client component, what you may be tempted to do is say, "Well, `use server`." I've seen some people do this. `use server`. Right? So this has nothing to do with server components. Server components are already the default in Next.js. So with `use server`, you're now creating a server action, right? So again, you may instead want to use that `server-only` package to ensure that this does not get imported in some client component.
Let's talk a little bit about dynamic routes, `params`, and `searchParams`. So mistake number 20 is actually misunderstanding dynamic routes, which will give you `params`, and then there's also something called `searchParams`. So here I modified our project a little bit. So here now I'm on a `/product/4` route. So this last number here is actually an ID. And this is very common, right? Where you have some kind of ID in the URL, and based on that, you, well, maybe you want to fetch data for that particular ID, right? So if I do number three here, we get our men's cotton jacket back. If I do number five, I get a different one. If I do number six, I get a different one as well, right? So this is called, right? So this is actually a dynamic route in Next.js. It could technically be any number here, right? So this is more dynamic. The way it works in Next.js, if you want that, you need to create `/products`, but if it can change, you need to write that part in square brackets, right? So here I'm calling it `id`. I could call that whatever I want. And then here we have another `page.tsx`. So this page will be used for all the different possibilities here, right? So if I use number 10, this page will be used. If I use number five, this page will be used. If I use number three, this page will also get used, because it would be too much work if we had to create a separate `page.tsx` for every possible number here, right? So that's not how it works. We just have one `page.tsx`, and you specify this here to Next.js with square brackets. Based on what's in the URL, we want to output something else, right? So we have to get access to that in that component here, and we can get that with the `params` prop. And so the `params` prop will hold the actual value from the URL. You do not get this prop in other server components, right? So you get it here in a page component, but not in, for example, our `Product` component here. This is a server component as well. I do not get `params` here, right? So Next.js does not give me `params` here. In this case, only here in the page component. Now, that will hold the actual number from the URL. So if I want to get access to that number in the URL, well, I can just pass it as a prop. I can, I changed this `Product` component a little bit. So now it's accepting an `ID` prop. So instead of always getting that number three like before, now it's just based on what's coming from the URL. So this would be a little bit more realistic, right? So let me actually just.
output that here on the page. Product ID params ID, so now we can see it here as well, right? So now if I make it five, it's five. If it's six, it's now it's do ID because that's how I named it here in square brackets. But if I call this blah blah in square bracket record, I can access that with dot blah blah, right? So whatever name you give it here is how you should access it here.
There's also something else called search params. So here you can do question mark, and then this is usually used for like different versions or variants or filters or sorting parameters. So here on a product page, you could have something like color is green. This is nice to put in the URL because if I would copy this and share this with somebody else, they can just paste that URL and they should also see the green version of of a particular product. I have a separate video on this, actually.
So we also want to get this from the URL. Now, to get this from the URL, you don't have to do anything with the file system. You can just get it directly here as well, and this is called search params. This is something that you also do not get in, for example, this product server component. It's a server component, but you do not get params or search params in here, right? So here you only get that here in the page component here, right? And then if I want to output that, you need to use search params. So then here we see green, and then based on that, I can do some logic or render some marker. And so these are two special params that you do not get in any server component. Usually, you're going to work with these with a page component. And just to type them properly, it always looks a bit clunky, but this is an example of how you could type them.
All right, now with these search params, you can run into some issues. Mistake number 21: incorrectly working with search params. So I changed the example a little bit. I removed the params, so we don't have a dynamic route anymore, but we still have search params here. In this case, we have the color here in the URL, and it's currently set to red. So this page here will receive search params. It will receive it right here, and then we can just say search params as color. That's why we see red here.
Now, I also added this color component here, and the only thing it holds is just these three buttons here, right? So here we just have three buttons, and when I click on red, we want to update the URL. So let me actually make it a little bit wider so you can see. Color is red. When I click red, it should be red. It's already red. If I click on blue, it should become blue. And we can do that in Next.js by using `useRouter` or the `Link` component, actually. But let's use the `useRouter` here. I can add something here to the URL, right? So I can say `/product`, then I can just, well, add the search param like this: `color=blue`, right? Just a simple example here. Here, color is green. And so that's how I can change the URL here. You can use `useRouter`, actually, the `useRouter` hook to update the URL, or you can even use the `Link` component, actually, to update the URL, right?
So updating the URL is typically not the problem. The problem starts when you want to get the search param from the URL, right? So it's always about reading and writing, right? So writing here is not the issue. It's reading the search param that you may run into an issue. So we can read the search param by using the `searchParams` prop in a page component. That's what we're doing here. The downside with this is, since this page component is a server component, it only runs on the server, right? This is a server component on the server side only. So when the user changes something in their URL and you want to be notified of that or you want to work with that on the server side, well, you're going to have to send that to the server side. So there needs to be a network request, and that is actually what Next.js will do when you update the URL. It will send a network request so that this page will receive the new search param and again render a new result.
Let me prove that here to you. So here, if I now open up my network tab, if I click on green, you can see there is a network request. I'm not making this fetch call myself. This is what Next.js is doing for me behind the scenes. You can see there's a request URL with some RSC identifier, and here in the payload, it sends the query string. That's so `color=green`. And it makes sense when you think about it, because if you want to work with search params here, well, this only runs on the server. Yeah, so it needs to be sent to the server, right? There's no way around that.
So the reason you may not want to do it like that is because you need to wait until you get a response back from the server, right? Now, here it's very fast because it's all local on my computer. But when you push to production, there could be a delay here, right? So when I click on red here, it may take some time before you actually see the result because it needs to make that network round trip. So when you're reading the search params with the prop, that's something that you need to keep in mind.
There is another way of reading the search params, which is with the `useSearchParams` hook. If you need the search param somewhere, you don't have to use the prop, actually. You can actually also use this hook. Now, remember, you get search params, and then actually you need to use a getter like this. And now we're getting an error because we're using a hook. So this hook will actually read from the URL client-side. That's not possible on the server. And so that's why in this case, if we want to use it like this, we have to make this a client component. So this works as well, but this will not send new network requests, right? So here, if I click, you can see there are no new network requests. This is another way of reading the search params. In that case, you do need to make it a client component, typically not recommended for the complete page, as we've discussed before. So this is just a silly example, but this is preferable in many cases over using the search params, right? So we probably don't want to use this hook directly here in the page component. You probably want to use it somewhere more deeply nested, towards the outer edges of your component tree, so you're not necessarily making a huge part of be affected by this `use client` boundary.
Now, if I go back one more time, so this will send a new network request. So there's an incoming network request with the new search params. It will render a new result, and since this is a server component, that RSC payload and the result of this render will actually be cached. It will only send a new network request if it's not in the cache, right? So initially, you may have a lot of new network requests. After you click around, you can see there is no new network request. So there is some caching. It's not the end of the world. It is something to keep in mind.
Let's talk a little bit about suspense and streaming. They sound very complicated, but as you'll see, it's pretty straightforward. It's also very innovative, actually. So I think these are pretty cool features in the latest Next.js. I changed a little bit, so now we just have an H1 and we have a link now to go to the actual `/product` route. So here I have one `/product` here, right? We have another page for that. And now this is where I'm doing the data fetching, right? So I moved it out of that product component. So let's actually go there. So here you can see I have my product page here. We still have that title, and I still have my favorite button here, right? So I just moved it to `/product`, and I'm doing the data fetching in here. But it doesn't matter, just to make this example a little bit clearer. Nothing wrong here, right? It looks well, pretty fast when I click there. It goes pretty fast, right? So here, if I click on "Go to product page," it's pretty fast, okay? Now it's fast because this is all local, right? So remember that when you're coding locally, everything will necessarily be pretty fast because when there's a request to the server side, well, it's on your computer, right? It's all being simulated on your computer, essentially.
Now, in the real world, when I go to the product page, there will be a network request to get that page. Next.js is actually just, well, a component. It's just a server component here, and that is the page, right? So this needs to be fetched, and then it will be displayed here. Well, that can take some time. And then in here, what we're doing is we're making another fetch call, we're awaiting that, and then once that's finished, it will render something. It will result into a so-called RSC payload, and then it will be sent to the browser again so that we actually see it. But it's all pretty fast because it's all local.
Now, let me actually add some delay here, right? So just based, this is just a way of sleeping, essentially, in JavaScript. Just a 3-second delay. We're awaiting that, so we need to wait 3 seconds before we can move on to the next statement, right? So now, if I go back here, if I now try to go to the product page, I will click right now, and now I'm waiting and waiting and waiting and waiting and waiting, and only now do I get a result. This is more realistic. This is mistake number 22: you're forgetting to think about loading states because you're coding locally and everything is fast for you, but that is not necessarily the case.
So what you want to do in this case is you want to use suspense, okay? So sounds very fancy, right? And the way to use that is actually with another special file. `page.tsx` is special, but there's another special file and other ones as well called `loading.tsx`. And whatever component you're exporting here as the default will be rendered while we're waiting there, right? So if I just say "loading...", this component, because I'm using `loading.tsx` and at the same level of that page, this one will be displayed while we're waiting for that page file.
So if I try this one more time, you can see now I'm getting "loading..." for those 3 seconds, a little bit more, and then finally, the product page component has finished rendering, and it will be streamed in, right? So if we have something else that will, if we had like a header or footer on the page, it will not be affected by that. It will just be streamed in, squeezed in between there. The RSC payload will just be rendered like that, and so then loading disappears again, right? So that is essentially suspense and streaming. So that suspense is essentially just a React component, but Next.js has abstracted that away from you with this loading convention, right? So here, when you use `loading`, it will wrap your page at the same level on which you're using the loading in a suspense component here. So page will be squeezed in there. So that's a much better user experience. So don't forget to think about the loading states.
All right, mistake number 23: not being granular with your suspense boundary. So what we have right now is, with `loading`, you have a suspense boundary wrapping the entire page, right? So if I go to that page, if I click on there, you can see I'm waiting. I don't see anything else. I don't see this H1, I don't see the favorite button, right? So let's say that the reason that we're waiting for it is actually because of this fetch call here. And so let's imagine that the fetch call actually takes 3 seconds longer, and we're waiting all the time for this for this data here that we only need for this product component. But meanwhile, it's also blocking the rest of the page, right? So when I do one more time, if I click now, you can see I would like to see the H1 as a user. It would be nice to show immediately that the user is going to be on the product page. I think that would be a better user experience, and it will also be nice to show the favorite button and other things on the page.
So in this case, we are essentially only waiting here for this product here. So what you may want to do is not fetch data in the page component. You may want to move it down to just where you need the data. So if I open this up, and so let's actually copy all of this and put it directly in here. And now we don't receive this as a prop. We just have it more nicely encapsulated here. We can remove this. And now, of course, we still want to have a loading state. So I can wrap this individual component with a suspense component, right? So with `loading.tsx`, Next.js wraps the entire page with a suspense component. That's not what we want to do because it blocks the entire page. And so I'm going to delete that. I only want to have a suspense boundary. I need to import this for this individual component, and you need to specify what you want to show as a fallback while it's being suspended, right? So basically, while we don't have a result yet. And that's why we can have an async component, right? So we're essentially waiting for that promise to resolve, you could say, right?
So now, if I go back here, if I refresh, if I go to the product page, you can see we immediately see the H1 and other things, and then we just see "loading..." just for that one specific component that actually needs a loading state, right? So we don't have to block the rest of the page in case you only were waiting essentially for something that this individual component needs. So you want to be granular with these suspense boundaries, right? So now, when I load the page, you can see we immediately see "product page" and other things, and then it's just that individual component that actually needs some more time to fetch data that will show a loading state. So this is a better user experience.
Mistake number 24: putting this suspense in the wrong place. So actually, very common. You need to put it essentially higher up than where you're actually doing the awaiting, right? So here we are awaiting a bunch of stuff, and this is sitting a little bit higher. This is why it worked. Now, if I would move it down into the component, so I remove it from here, and now you may think, well, I need to make it part of the component, right? It would be maybe a little bit better capturing it like that, right? Then here I removed it from here. So now wherever I use it, it's always wrapped in a suspense boundary, right? So that's how you can view it. Maybe it makes sense when you think about it, but unfortunately, when you try doing that, you can see I clicked here, but I'm waiting for the entire page to load now. So this unfortunately does not work because it's kind of sitting below where you actually do the awaiting stuff. So it needs to sit higher than that, right? So you actually do need to wrap it like that. When I click right now, you can see we have "loading...".
Mistake number 25: forgetting the `key` prop for suspense. This is actually a tricky one, and you'll run into this sometimes, especially if you're working with, for example, search params. So I just changed the example again a little bit here. So we are reading the search params from the URL, and that's what I'm passing to that product component, right? So then it can get the right title for that particular ID. And I have nicely wrapped this in a suspense, right? Just like we saw before. Still have my favorite button here. Here I have some links. I could also use buttons with `useRouter`. I'm using the `Link` component this time. Doesn't matter. I'm using the `Link` component this time to to update the URL. I'm using suspense here, right? Looks good.
So now, when I load the page, we indeed see "loading..." here while it's fetching that data, right? So here we're still fetching the data. I actually changed it to 2 seconds waiting. That and this may also take some time. So then eventually you get a result, and that will be streamed in, right? That will be streamed in here. So in the meantime, while we're waiting for that, it's going to have a paragraph in the HTML, okay? So all of this is working. When I load the page, you can see it's working, okay?
Now, what happens if I click on "View product with ID is 4"? If I click now, I just click. Nothing is happening right now. Nothing is happening. Only after a couple seconds does it get updated. If I click on "View product with ID 5", nothing is happening here. And the reason is actually that React doesn't know when the ID changes that this suspense should be re-triggered, right? This suspense right now is only triggered when we just load the page. After that, it's not going to trigger again. You need to tell React, "Hey, when the search params are different, we want to trigger suspense again." So you should consider it to be a different product. It's going to be a different product when the search param is different.
So the way that you specify to React that something is different is with the `key` prop, right? So we also see it often when you do like a loop in React to render something, but here you sometimes you also want to use it to tell React that something is unique. So here it's a unique by ID in the search param. So when that changes, it will re-trigger that suspense. And so now, when I load the page with ID 3, we indeed see "loading...". But now what happens if I click on this one? If I click on number 4, you can see it gets re-triggered. If I click on this one, you can see we see "loading...". Right? So this is actually a really tricky one. So don't forget to use the `key` prop in case you want to re-trigger suspense.
Let's talk a little bit about static and dynamic rendering. So I changed it a little bit again. So I have this homepage here that's just this one page, nothing fancy going on, just like what we had before. And we still have a product page, also nothing fancy going on, just an H1. So these are the two routes I have right now. Now, these routes that I have here, we're not using dynamic routing, right? So dynamic routing is if part of the URL can be anything, essentially, right? So we use square brackets for that. That has to do with routing, right? Dynamic route.
Now, there is also the concept of dynamic rendering and static rendering. Let me show you that. So here I can run a build. What I can do here is I can say `npm run build`, and it will create an optimized version of my app. And one of the things it will do, you can see here, "Generating static pages." It will go over all of your pages. So in this case, well, we can actually see it here, right? So when you run `npm run build`, you get some useful output here, actually. So you can see my homepage has been, there is that empty circle icon, and that means here it shows you the legend here below. It says "Static pre-rendered as static content." So what that means is that this homepage here, this homepage component, it was run. This ran, and it created HTML out of this, which means that if somebody goes to the homepage now in production, this component doesn't have to be rendered again because the HTML has already been rendered here as part of the build, right? That's optimal. The HTML that was created here, and actually Next.js will put it in a folder here in `next`. So there's some HTML for that particular page in here somewhere. We can actually put that on a CDN, so every user that will go to the homepage will just immediately be served that HTML page. This component here does not have to be run again, right? So that's static rendering, which is actually what we want because that's optimal. We can just run this once and then just serve it to everybody that comes there. We don't need to run this again, right? So that's static rendering. And we can see here that `/product` is also static, right? You can see that symbol here. So this page, right, isn't doing anything fancy, so it makes sense that we can already create HTML out of this and then just serve that whenever somebody goes to `/product`, right? So we can just serve the same HTML over and over again. This does not need to run again, right? Because we're not doing anything fancy here. We can just generate HTML once out of this and just put it in the CDN and just serve it over and over again. And so that's static rendering. That is what we want. And you can see, actually, by default, Next.js also creates a "not found" page, but we can ignore that for now.
Now, there is also something called dynamic rendering. So what if we cannot just generate HTML out of this once? Maybe it depends on, let's say, the user who is logged in, right? So we may want to generate a different homepage, maybe with like their avatar on top, for the user who is logged in. In that case, we cannot just generate that once because it depends on who's currently logged in. Could be many different users, right? So we cannot just generate the whole page once. In that case, we have to do dynamic rendering, as it's called, which means that when there is an incoming request, we will not just give HTML immediately back. We're actually just going to run this again so that it will be tailored to the currently logged-in user, which will take more time, but there's no way around it. So if it's possible, you want to keep that static rendering because it doesn't have to run over and over again, right? So that is static rendering.
Now, the other option is dynamic rendering, which means every time there is a request for, let's say, the homepage, there's no HTML ready yet, right? So then it has to run this component again for everyone that makes that's making a request. It has to run it over and over again. So if somebody goes to the homepage or `/product`, there's a request. When I press enter there, and so here Next.js will run all of this again, and that's dynamic rendering. This isn't rendered once, it's rendered whenever there is a request at request time, as they call it. So that is necessarily slower, right? So if you have static rendering, you already have the HTML ready. Dynamic rendering, you have to do it over and over again, so it's going to be slower and also cost more compute. Ideally, if it's possible, we want to keep it static.
So mistake number 26 is accidentally making it dynamic. The way Next.js works is that if you use certain features in Next.js, it will automatically opt the route out of static rendering and make it dynamic rendering. So that is, for example, the case with using the `searchParams` prop. As soon as you hook into that `searchParams` prop, if I save here, and let me type this properly, so now if I use the `searchParams`, maybe I use it somewhere here, right? So maybe we expect there to be maybe some color in the search params, right? So actually, I don't have anything in the URL. It doesn't matter. It's just an example. So now, if I run another build, let's see what we get. `npm run build`. And so I added this to the homepage, to the homepage component. It's going to generate static pages, as you can see here. And now when we look at this output here, you can see we have another symbol here for the homepage. It's now, as we can see here, it's dynamic. It's server-rendered on demand, which means every time there's a request for that home route, there is no HTML pre-rendered. It has to be rendered at that moment as the request comes in, over and over again. You can imagine that's not as efficient, but that's what you get automatically when you use the `searchParams` prop, right? Be careful with that, right?
So the other option you have is using that hook, but then you have to make it a client component. Let me remove this as well. So the other two common ways that you accidentally, or maybe deliberately, sometimes there's no way around it, that you can opt your route into dynamic rendering is when you use the `cookies` function that you get from Next.js or the `headers` function. Maybe you're using these for an e-commerce cart, cart info in there, or maybe the user's preferred theme. So be careful with this, because whenever you use these two, let me actually do it in the other page that we have. Let's actually do it in the product page, just to prove it to you. Now, I don't have them in the homepage anymore, so homepage now should be static rendered, statically rendered. The product page should be dynamically rendered. So I'm going to run the build again. You can see "Generating static pages."
All right, so now we can see that indeed, homepage is statically rendered again, but the product page is now dynamically rendered, which makes sense when you think about it, because we don't know what the cookies or headers are going to be when we just when we just run `npm run build` right now, right? So this depends on runtime information. As somebody makes a request to `/product`, that's when we want to get the cookies and headers. And so when you use these, of course, you're interested in the actual request that's coming in. So of course, this cannot happen statically, right? So this needs to happen during runtime, and so that's why it's now dynamically rendered.
Now, when I show you this, you're going to say, "Oh, yeah, it's easy, right? Cookies and headers. When I use it in a page, it's going to be dynamic." Okay, makes sense. And so this is still pretty easy. Now, in practice, you often actually have an even bigger risk because you're not going to use it in just one page. You may actually use it in a header, right? So here, for example, I'm using cookies to get all the cookies from the incoming request, and maybe we have some cart information in there, so that we can show in a header, perhaps how many items the user has in their cart. And here I would output "number of items in cart," right? Just a silly example. Now, here it's even more dangerous, you could say, because a header, you're not just going to include in one page. You're often going to include this app-wide, right? So here in `layout`, I would want to display this on every page. So I would naively just include it like this in my layout here, right? So now I have a header here, and this header, remember, is using cookies. This is a dynamic function, dynamic API, as they call it, which means that now every page will be dynamic. We lose all of the benefits from static rendering because the header will be on every page, right? So here I have my header, logo, number of items, and cart on the home route, and then on the product route, I also have this. So now, when I run a build, let's see what we got. "Generating static pages."
Okay, now you can see everything is dynamically rendered, right? Just because of including this in one little component, essentially. But that component is included everywhere in my app, so it's now all dynamically rendered. Now, even that, you could say, "Oh, I was using cookies, the `cookies` function, it's pretty obvious we were using that, so it shouldn't be too hard to prevent that mistake." Now, in practice, you're often going to use third-party libraries. So, for example, if I'm using Auth0 here for my user authentication, I think it's a wonderful solution for authentication. I'm a brand ambassador for them. It's a paid sponsorship, but I would also use Auth0 for my own projects even if they weren't sponsoring me, right? So it would be something like this. You get, you get access to the user information in there, and then you can maybe show the user's email in the header, right? Or maybe the user's avatar image. That's something about the user. And this header, again, I'm using, I want to show that on every page, everywhere, app-wide. So I'm including that everywhere. Now, here I'm not using the the `cookies` or `headers` function myself, but that third-party library may use that under the hood. Not just Auth0, any authentication solution is going to use that because they need information from the incoming request to get the user who is logged in. They have to do that. So if you're using some kind of authentication solution, be careful with where you use it, because if I now run a build, let's see.
So you can see now it's still all dynamically rendered because here they are using cookies or headers under the hood, and they have to do that. There's no way around it. That's so as your developer, your app, it's sometimes a good idea to just run a local build so you can see which routes are being properly statically and dynamically rendered. And sometimes there's no way around it, but sometimes you made a mistake and a route is unnecessarily being dynamically rendered. So right, so you want to be a little bit careful with how you're structuring your application. And because of this, actually, often you also don't want to do the authentication check, right? So here you could do it inside the page component. Now, if I remove the header from my app, you would think this page is going to be statically rendered again, right? Right? So something like this. And then if I would run a build again, you may think, "Oh, now it's going to be static again." But you can see here, even the product page, it's still dynamic. Now, because here I'm doing my authentication check, and it will, it will need information from the cookies or header. And so typically, you actually do want to do the authentication in middleware. And actually, I have a complete video with authentication in Next.js with Auth0. So check it out. And of course, I also recommend you check out my React in Next.js course, in which we're actually building some projects from absolute scratch, so you can see how everything fits together.
Let's talk a little bit about working with secrets. So here I have a secret API key, and I'm sort of hardcoding that here in this file. I could also write it in the component, but typically these constants, you write it outside the React component, and this is actually a bad idea in my view. Mistake number 27: hardcoding secrets in your server components or outside of there in the same file. I don't think this is a good habit to get into, and let me show you why. So here we have the `Price` component. I'm not using it anywhere, right? So now, let's say I'm going to add it to the page. And so just added to the page component, which is also a server component. So now I have `price` here. Now we have the secret API key. Doesn't mean that now, since it's visible on the page, that we also leaked this API key to the browser. Let's actually inspect here and let's go to sources here and then and then here I can actually search for that. So here I don't see it here. So it looks like so far so good.
Now, the thing with React components is, of course, that you could also use them in multiple places, right? Typically, you want to make them reusable, and sometimes you will actually reuse them in some other place. Let's say in this `FavoriteButton` component, which is a client component. We have to use `client` directive at the top, which means now if I use it somewhere in this file and I actually import it into this file, now this also becomes a client component, which means that its code will be shipped to the client, right? So now, if I save here and I refresh here, and now if I search it, you can see I leaked my secret API key to the browser, right? So this is the danger of sort of like hardcoding it here, right?
So instead, you do want to use environment variables, right? So Next.js actually works with a bit of a convention around this as well. So if you read the documentation, they actually recommend using `.env.local`. And actually, they don't mention just simply `.env`. So maybe traditionally you were used to using this. You can still use this, but this one is not automatically get ignored, right? So here, if you're going to use `.env`, you do want to make sure you add it to `.gitignore`. Sometimes I use `.env` because Prisma, for example, uses it, and so it's easier to stick to `.env`. I find, but without Prisma, I would probably just use `.env.local`, which is a bit like the default in Next.js. This one is properly get ignored, as we can see here. So that's fine, right? So here I'm using Auth0 for the authentication examples, that's why I have this here.
Now, I want to put this secret API key. Let's see. I don't need to use quotation marks here. So now, if I want to use this, right? So if I just remove this, let's say I'm just, let's say I'm actually just going to use something silly. So now I can use `process.env.SECRET_API_KEY`. And so now, if I'm using it like this, let's see if we can still find it in the browser. So I'm going to refresh here, and now you can see it can't find it anymore, right? So this is a little bit safer because Next.js will actually not include your environment variables when the code gets shipped to the client. So this price is still included here in the client bundle, but Next.js can see there's an environment variable in there, so it will not send this. If you do want to send it, if it actually needs to be visible on the client, you need to make that very explicit here. So you have to tell Next.js, "Hey, `NEXT_PUBLIC_`", and then I also need to prepend that here to the name. So now, if you actually include this and refresh, now you can see it has been sent to the client as well. And so with these environment variables, you get some additional protection. You actually need to make it explicit that you want to make it public. Environment variables by default will only stay on the server, right? So don't do something like this: `use server`. We saw it before. If you want to keep it on a server, you can just make it an environment variable. And when you include `NEXT_PUBLIC_`, that's when it will be included in the client bundle, meaning it will actually be visible in the browser. And by the way, if you make the environment variable actually part of the render output, right? So now actually put it here as part of the return statement, then it will be visible. So if you do this way, you will actually make it visible in the browser because you intend to display whatever you return on the page here, right?
Mistake number 28: not making a distinction between utilities on the client with utilities that should only be used on the server side. So we just learned a little bit about how these environment variables, they have some protection built in. So we just learned a little bit about how you want to store your secret in these environment variables. So now, let's say I have some utility function to get data, and so you can use it in multiple places. That is typically why you create a utility function. Now, I could use it in a server component, right? Somewhere, somewhere here, but I could also use it, maybe on accident, in a client component, right? So if I would use it in a client component, and on click, I want to get data. Now, remember, this utility function is using an environment variable, and we actually just learned that Next.js has some extra protection here. So if you accidentally use it in a client component, it doesn't mean that this, that the secret will be sent to the client when you actually make it like `NEXT_PUBLIC_`. `NEXT_PUBLIC_` will it be sent to the client? Very often, we don't want to show it on the client, right? We want to keep that on the server. So we actually don't want to add `NEXT_PUBLIC_` here. So what will happen is, if you actually do use it on a client, Next.js will not send this secret API key with it, and therefore you may get unexpected results, right? So it's not so much that you're leaking the environment variable, it's that you just not going to get the result that you're looking for because this URL will not include the proper API key on the client. To prevent that, this is actually where the Next.js documentation recommends that you actually do use that `server-only` package, right? So `server-only`. So this function now, you will get an error if you try using it on the client side, right? So if I try using that here, for example, all right, so now I will get an error. You're importing it, say, a component, but it's actually a utility function that needs `server-only`, that only works in a server component, right? So here, now in a client component, I get that error. I can fix my mistake. Oops, mistake is fixed. So this is now only usable on the server. And actually, to make it clear, you may actually also change the name of this function into `serverUtils`, so it's clear for every developer working in the project that these utilities are only meant for the server side. And so the `server-only` package is probably more common to see with utility functions than with actual React components, right? So this is also what the Next.js documentation shows you.
All right, last one. Mistake number 29: using the `redirect` function from Next.js in `try...catch`. Very often, when you're doing some kind of data fetching, for example, whether it's with the `fetch` API or with your ORM, Prisma, lots of things can go wrong. Actually, very often people will wrap it in a `try...catch`. Now, `product` is not defined here, so I do have to change this a little bit. And then if there is a problem, we will just log it, right? So so far so good. And typically, actually, you would do the data fetching perhaps in the utility function. I'm doing it directly here in the server component. It doesn't matter. Here, I'll just leave it like this. So so far so good.
Now, now pretty often, actually, what happens is, for example, if there is no product, maybe you want to redirect the user, right? So here I can use `redirect` here from `next/navigation`. Only works on the server side, and so this will not work on the client side. And what we can do here is we can say, well, if there is no product, we want to redirect the user to, let's say, "not found," perhaps, or maybe we have a special page, "no product page," or maybe maybe the user needs to create a product when there is none. Something like this. The point here is I'm trying to redirect the user. Now, this is super tricky, but this will not work as expected because what Next.js does with the `redirect` here is it will actually throw an error, right? So it's basically like `throw new Error`. This is what Next.js does under the hood. And since we are in a `try...catch` block, when you throw an error, it will simply be caught here, right? So it will not stop execution here. The execution here in a server component, it will just be caught here, and then we go into this block, right? We log some error, and then we just continue, actually, which is not our intention, right?
So let me actually show that to you. If I actually, because there is actually a product here, so just to let this run here, if I save here and refresh, you can see I'm not being redirected here, even though, well, here there is a, there is going to be a product, so we should be redirected. It's because I'm catching it here. And so if you actually want to redirect a person here, you need to do it outside a `try...catch`, right? So make sure you use `redirect` typically outside a `try...catch`. So if I save here now, you can see I was actually redirected, right? So now, if I go to the homepage, you can see I'm being redirected to create product. So now the redirect actually works, right? So if you do something like this, it won't work. And sometimes you're not going to use the `redirect` function yourself. You're using a third-party library, like maybe for authentication, right? Maybe you're using Auth0, maybe you're using NextAuth, and those third-party libraries may actually also use `redirect`, for example, redirect to login if there is no user in some request, right? So make sure in that case that you are aware of this issue with `redirect`. Very tricky.
Well done for making it all the way to the end. You're probably a little bit confused about some parts. That's totally normal. There are a lot of new things in Next.js, a lot of new paradigms, and it will take some time to get used to that. So hopefully this video helped you out a little bit. I want to thank Auth0 for sponsoring this video. I'm Wesley, by the way. I'm a brand ambassador for Auth0. It's a paid sponsorship, but a great solution for authentication. And I'm the creator of the professional React and Next.js course. So in case you really want to master React and Next.js, highly recommend you go through that course. Really high-quality course, some amazing projects, my best work so far. Highly recommend you check it out as well. So in any case, thanks for watching, and I hope to see you in the next one. Bye.