10 common mistakes with the Next.js App Router

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I've talked to hundreds of developers building with the xgs app router probably looked at over a thousand different repositories and here are the top 10 mistakes that I've seen that hopefully can help improve your next nextjs application let's get into it okay first is thinking you need to call route handlers from your react server component now I have to step back a little bit here and talk about what a route Handler is in the first place a route Handler allows you to have verbs httv verbs like get post put and delete for example and have a API exposed that allows you to get some data or mutate some data now for folks coming from the pages router World they might think wow this seems basically like the same thing as API routes and that's usually where the mistake comes in you make a route Handler like this which is a g it returns some Json response that is hello world and I go back inside of my page I mark it is async I say okay I want to have fetch to localhost sl3000 API I'll get back this Json data and then I'll printed out here in the H1 and this does work but it's actually kind of a mistake because for two reasons one you have to hardcode the full URL because you're here in the node.js context but two you actually don't even need that extra step or that extra Network request because this runs securely on the server as well as this running cely on the server instead you could just put that Json here you could just call that await get user or whatever that function is here whatever that promise is directly without needing to use the route Handler in the middle so typically when I see something like this I would cut it and it would be let data equals away either fetching to somewhere externally and not having the route Handler in between or just calling that function that you've abstracted out here as well too so that's number one okay building off this for number two let's talk about the defaults for Route handlers this is another place that I see people get mixed up so let's say you did need a route Handler for some reason I think most of the time when I see people actually needing route handlers it's for handling web hooks or for handling some sort of external request in their application maybe it's for scaffolding out something like next off for example but they create a route Handler just to test things out so they make something like this and they're wondering why let's say I put in something like date and let's do yeah let's do something like this so I have a date and I have a new date and a local string of New York now if I go to localhost 3000i I'm going to see this new date string and if I reload the page I'm getting a new date on every single request now there's two opportunities here to better understand how this model works the first is understanding the difference between local and production so in local even though this data is Cash which I'm going to get to in a second you're going to get a new value it's going to get recreated on every single request and secondly is understanding why this is cashed by default so let me show the first one let me open up my terminal and I'm going to run um run build and run starts so I'm going to do a production build of my application and then I'm going to start up my local Dev server my production server on my Local Host so now if I go back to here and I reload the page it never changes so this is a difference between how your local uh your local Deb environment is working through next Dev versus doing that production build and then serving your production server now the question is why does it work this way well this is actually pretty powerful if you understand more of the intention behind it a route Handler is the base of how Pages work inside of the app router as well too and Pages can be cached by default so even if I'm doing an asynchronous component or a server component and I'm fetching some external data that doesn't necessarily have to run on demand on every single request it's kind of like instead of a server component it's a data component because you can fetch data you can pre-render a page that only was pre-rendered on the server and then you don't have to have any additional client.js for that long story short the options for how route handlers work are the same as pages so by default this is cached static which means that anything that I do here to opt into making this Dynamic it will then work the same way as a traditional API route so for a git for example if I were to read request. something so maybe I want to read the incoming search parameters or maybe I want to read uh the headers that would opt it into being Dynamic you can also use the helper functions that we have headers as well as cookies that will read from the incoming request or more commonly what I see is that folks are doing a post request so if you're doing a post request because you're probably um maybe it's something with web hooks again post requests will be dynamic by default as well too now the reason that this git pattern and this is very interesting is because maybe inside of here I want to do let's say data equals await fetch um wow shout out to Luke who has their user named on here and their amazing contributions to open source um let's say I want something like this and then maybe I'll do this and let me just go back to my Deb server here and we're going to see that it prints out some information about my my GitHub profile now the really interesting thing here is in the pages router model having an API route required that you had a server but there's also an option in xgs that allows you to do a static export and basically take the generated files and put them anywhere put them on a server somewhere um put them on an S3 bucket somewhere it doesn't have to be something that's always on right so the cool thing about this again if I do the build and I start it up since I have the ability to Define this arbitrary route that can generate maybe it generates Json maybe it generates an XML file maybe it generates a text file I can do this and it can still be compatible with that static export model so this would work and even if we wanted to do a static export we can still use that now I will say most of the time you probably don't need to do anything like this with route handlers because you can just call that function directly on the page the server comp component instead okay third is thinking that you need a route Handler because you're using a client component so so far I've talked about server components and your next question might be yeah but I'm using route handlers because I have client components let's talk about that one as well too so I change my page to be a client component I added a form and a button that takes some Advent Handler for onsubmit and then in onsubmit I just preventing the default and I'm logging submit so inside of here what you might say is okay so now I need to make a fetch to Local Host API have my route Handler and then I'm in that tricky State again with having to set up route handlers when maybe I I don't need to so this is what happens I click send it I see in the console I hit submitted what if I told you that you didn't need to do it this way and you could skip writing all this additional code and just call a server action instead yes server actions can work with your client components as well too so what if if we delete this and we take this and we say you know what action is going to equal send and we import send from our actions notably we're importing it here and we're not putting it in line inside the file this is a use client file we're in the client uh bound we're on the client boundary here versus this actions on the server so inside of actions we have used server we have this function send it and if we go back here hit save click send it now you notice the logging position was different too I didn't see my client side event handler say that it was submitted in the browser instead it just directly called the server and said send it which is where I would have that logic that was in my route Handler in the first place so this can drastically simplify things uh if you want to take this further as well too there's some cool things you can do with use optimistic and use form State use form status some other built-in utilities to make working with forms better in nextjs so definitely check that out too okay fourth we have using suspense with server components and I'm going to talk about this through the lens of partial pre-rendering which is an upcoming addition to nextjs it's out experimentally right now but you'll start using suspense more and more in the future so it's helpful to start thinking about this now so in this partial pre-rendering demo I reload the browser I see these loading States this is all defined by my suspense boundaries which is super super helpful so understanding how suspense works is a big unlock for the future of nextjs so let's talk about it let me move this back here what I have is a server component it's marked async and I'm making a call to fetch a list of blogs and then I've enumerated them and listed them out on the page so for example if I said Okay I want this function let's just say this is blog post and we're going to say export default function page uh and maybe I want to have some additional information on this page so I've got this section for my page I've got an H1 I've got blog post that all looks fantastic now in a partial pre-rendered World you'll be able to pre-render all of this right now you're pre-rendering everything because there's no suspense ball back but there's a reason why you're moving this data fetching closer to your UI is because this will be pre-rendered when you run your build so you might say okay that means that I want to use suspense with my blog post so that I can have a fallback and let it get pre-rendered or have a loading state so you know what let's do this let's wrap let's do um suspense we'll import that we'll have a fallback cool so we have this fallback loading and then we render this here okay so I reload the page but nothing is happening now the mistake here and I don't know who would make this I've definitely never made this mistake just kidding I've definitely made this mistake uh is that you want your suspense boundary to be higher than your data fetching component seems obvious in retrospect but it definitely happens and I I can understand how people fall into this so in reality you'd want something like this now not only can my blog be pre-rendered in the future but also the fallback State you probably wouldn't say loading in that world you might have um you might have it be empty or you might have a loading skeleton for example but yeah this is this is the difference here now what you're wondering is okay well why am I not seeing that state and that's because this by default is being cached so another thing to prepare us for this partial pre-rendering future is that I can say I want to make this Dynamic and right now this function is called unstable no store uh so I can import this here I can reload and now we're seeing this Flicker and that flicker means that this bit should not be cash it should run dynamically and that means that we are seeing the suspense fall back just to make it more dramatic you see loading as well too so hopefully that helps uh we'll be talking a lot more about suspense in the future but at least for now as you start thinking about your application structure as you start exploring suspense helpful tip to know I think okay the fifth tip here the fifth mistake that I see sometimes is understanding how you can use information about the incoming request in your server component so maybe you want to forward headers to a fetch for example you can use the headers function to read those headers or maybe you want to read the cookies for example because you want to look at an authorization cookie that you've stored or JWT that you've stored um maybe you want to look at the parameters or the route segment parameters in your url those as well as the URL search parameters are actually props on the server component so you have params and you have search params so if I wanted to for example let's say inside of here instead of this let's do a H1 and we're going to read search par dot let's say hello awesome so inside of here I'm going to make a new search program hello and I'm going to say world and now I'm able to read that incoming information from the request or I could read the prams if I have Dynamic route segments in my URL I can read those as well too so there's a couple different ways you can read from that incoming request using some of these built-in functionalities in nextjs okay six and seven for most common mistakes are how to use context providers and where you place them in your application sometimes placed incorrectly and the second is the in weaving or the inter leaving between server and client components so for example if I have my root layout here on the left you might be thinking okay I have some components I have some dependencies that require react context where do I place that provider in my application do I have to put it around the lowest component in the tree do I need to have it at the top of the tree does that mean that everything below it is going to be a client component well this pattern it's explained in the docs I want to visually show it a little bit here let's imagine that I have some theme provider here and this is using react context now this can take in some children and this is a client component but the ideal place to place this is actually in the root layout so what we'd want to do like we show here is actually take this and I think I have my file name differently I think it's called providers but we would actually want to take that theme provider wrap it around the children of our root layout so wrap it around the children of our application this is still a server component and that still allows children of this like the page for example that still allows this to be a server component so the child of a server component can be a client component this works as we would expect this actually goes into the next most common mistake I see which is when you apply used client does that apply for everything does that apply to its children and how does that relationship work and I will say this is all new so while I'm framing these as mistakes I think they're all learning opportunities for us so don't feel harsh you know I've made this I've made all of these mistakes in this video um so this top level layout is a server component but then the child of it is this children prop here that goes to the page so the page is the child of the layout now the child of this page is this button component let's take a look at this button component this button component is a simple client component that has a counter so it has some State you click on it the state increments and we see this new uh this new state reflected in the button text so this is already showing how you can weave server and client components but the question then becomes what about the children of this component so for example let's say that instead here I'll just do a fragment and then I'll have this but then I'll also have another button and this other button is also a client component so I have this button I have another button and another button here do I need to explicitly have used client at the top well the answer let's find out the answer is no and the reason you don't is because you're already in the client boundary here so the child of this component are going to be client components unless for example I take some children and let's say I want to have the children here you can still weave in the server components as well too so then maybe here for example I know this is kind of a contrived example but I think it's still works this is a client component this is a client component this is a server component and they can all work together now the big opportunity here and why yes it's a mistake but it's also an opportunity is that we need to have Dev tools that help you visualize these things and we're definitely working on making this experience better so that you can understand the state of how your server and client components work together okay two more second to last the mistake I see most commonly is just forgetting to revalidate data after a mutation so in the nextjs repo we have this fors example which I'd recommend checking out it shows a server component that fetches a list of to-dos it renders out the list of to-dos it has a form to add a new to-do and inside of this form it uses a lot of the latest features like server actions so I have an action to submit the form to add the new to-do and what I see a lot of folks do is they start using server actions maybe they're moving from the older model where they were trying to map it one to one to Route handlers and they move to this and they hit the button and they hit submit and they don't see that list of to-dos update and they're trying to figure out what exactly is happening well what they're probably missing is inside of their action in this instance I have a SQL statement here that's inserting a new value into the to-dos table what they're probably missing is some revalidation either revalidating the path or by adding a cach tag onto the fetch or onto that bit of data and then revalidating that specific tag so pretty commonly I see this I know that caching is still very new right now and a lot of these are new patterns we're also working on making caching a little bit easier as well too but this is one that I see pretty common okay and while we're here we can talk about the last one which is throwing a redirect inside of a tri catch so I can understand how this happens I definitely made this mistake myself as well too you're inside of here you've revalidated your path and you say okay awesome now I want to redirect back to the index route for example well internally the way this redirect is working is it's actually throwing an xgs specific error so you would actually want this at the conclusion here either that would be after the try catch or in like a finally block um and I think most common where I see this is either in a server action or if you're trying to use it from a client component so I recently updated the docs here to show an example when you're using a server component for example rather than trying to do a redirect inside of an event handler you would want that to be inside of your server action if you're wanting to do a redirect on the client side in an event handler you'd be using the used router hook which does have the ability to programmatically route and programmatically redirect okay those are 10 common mistakes or common opportunities to learn more about the nextjs app router that I've seen I'm sure there's others and we're definitely working on making some of these more obvious both in the framework as well in the documentation so would love to hear what other things you'd like to see me talk more about or go more in depth on any of these as well too peace
Info
Channel: Vercel
Views: 132,219
Rating: undefined out of 5
Keywords:
Id: RBM03RihZVs
Channel Id: undefined
Length: 20min 37sec (1237 seconds)
Published: Mon Jan 08 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.