Next.js: How to Migrate From Pages to App Router

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I just released an update to my big next JS with Express and typescript course where we migrate the app we built there to the newer next.js app router in case you don't know the app router is a new way to build your next JS projects and it replaces the older Pages directory the app router is built on react server components which is a new feature in react we will talk about server components in more detail later in this video but when I first made this course the app router was still in battle so I decided to build a project with the Pages directory which has been stable for many years and will by the way also be supported for many years to come so the Pages directory will not go away but I promise that I create an update for the course where we migrate this project to the app router and the cool thing is by building the project in the Pages directory first and then migrating it you really understand the differences between these two approaches and how they relate to each other both the Pages directory and the app router can do the same things but how we do these things like server-side rendering and caching is different between these two approaches and knowing both approaches is of course also useful because there are still a lot of legacy codes built with the Pages directory and some people even prefer using the Pages directory for new projects now because the app router still has some problems however the app router has been stable for a while the next JS team had enough time to get rid of the biggest bugs and it's working quite well now and I spend so much time building projects and studying the app router that I feel very confident that I understand how we are supposed to use it properly in this video here I want to talk about the changes I had to make to convert the project on a bit of a higher level I will put a GitHub Link in the video description below where you can see the differences between the project with the Pages directory and the app router migration so I put this GitHub page there and here you can see all the changes we updated the packages and converted the whole project and some of these files just move to a new folder they didn't change but a lot of other files changed so you can check out this GitHub link and see all the divs and if you want to check out the course go to coding and flow.com nextjs or nextgs.coding and flow.com they both point to the same page here you can find the landing page of the course with the whole curriculum and it's over 27 hours of content now we built a whole blogging website with tons of features with user authentication our own backend server and a common system and all this kind of stuff and yeah we build it with the Pages directory first and then we migrate the project step by step to the app router and here you can see the single steps involved and yeah in this video I want to compare the code and talk about the changes on the higher level as you can see the whole migration takes around 3 hours in total but that's of course with explanations and everything and when you take a look at the documentation of next year as you will notice that there is now this drop down here on the top left where you can select the app router or the pages router the functionality and the stuff you can do in these two different routers is pretty much the same just the approach and how you write your code and structure your project differs between the two but it's interesting to note that the pages and the app router are interoperable you can have both within the same project the only thing you will notice is that when you navigate between these two routers within your app you get a hard refresh of the page so it navigates like a real link a real angle attack rather than a next JS link which maintains the layout and enough behind everything so it's almost as if you were navigating between two different apps but of course it's still running under the same URL and you can share data within the same app and verzelle already confirmed that the pages router will still be supported for several major versions of nexjs which means separable years basically the next JS documentation also contains this migration guide from the pages to the app router with some steps involved but this is a very basic migration type it doesn't cover quite everything you need to know but if you read the rest of the documentation about routing and fetching and especially caching then of course eventually you will understand the whole app router but the first time you build a project with it it needs some getting used to but once you understand it it's actually easier to use than the Pages directory in my opinion because a lot of steps to do a server-side rendering and caching have been simplified and generally you also have to write less code to achieve the same thing nevertheless it can be a bit tricky especially in the caching part trips a lot of people up in the app directory but in my next JS course and also in the next JS tutorials I have here on YouTube I explain how caching works so just check them out and this will help you understand this the app router has been built around react server components which are a react feature this is not specific to next.js other reaction Frameworks can also Implement these react server components but as far as I know next JS is the only framework right now which implements these new server components so next JS is basically the only way to use this new feature right now these server components are rendered undeserable so they have a similar purpose towards get server-side props and get static props had in the pages router so get server side props and get static props they don't exist anymore in the app router we will take a look at the code of such a react server component in a moment but you can basically imagine them as a more powerful but at the same time easier to use where to achieve the same thing as get static props and get server-side props the main benefits to what we had before is the smaller bundle size because this react server components are fully rendered on the server no JavaScript of the packages you use in your code has to be sent to the client only the Finish HTML of the page has to be sent to the client whereas in the approach we had before in the pages router I think it still send all the JavaScript to the client only the page was pre-rendered on a server to be honest I don't fully understand the differences between how exactly this rendering on the server works I only know that server components are supposed to send much less JavaScript to the client and therefore these Pages load faster especially on less powerful devices and especially if we aesthetically cache the page so that all the data is fetched when we compile the project and then we only send the finished HTML page to the client when they access them another cool feature is streaming so in the old approach with get server side props we could load the whole data of the page on the server in advance right we loaded the data in get server side props and passed it to the component but this was an All or Nothing approach you had to fetch all the data that you wanted to load server side at once in this function whereas now we can actually segment our server components and they okay a certain part of the page takes longer to load or maybe it's a bit slower to load than the other segments but we don't want to wait for this part so let's show a loading indicator instead and then when the section has finished loading we can load it from the server in hindsight or stream it in this is why this is called streaming it streams this result from the server to the client but the content of the page is still coming from the server which is better for SEO because this way search engine crawlers can see this content and it also loads faster than loading and rendering this data client-side besides server components we also have client components now since server components are completely rendered on the server they don't provide any interactivity we can't add click handlers and server components we can't use state or effects in there we can't use client-side API eyes like local storage we can't use context or any react hooks because all these server components can do is loading and rendering components on the server and showing us the finished page the finished HTML whenever we need interactivity we can turn our component into a client component we do this simply by adding this use client so-called directive at the top but it's just a string at the top of a component file when you add use client everything below this directive turns into a client component and everything you put into this client component is also a client component now here's the thing in next.js these client components are still pre-rendered on the server and in fact in the pages router all components were client components effectively so it's not like client components are terrible for SEO they are still pre-rendered on the server they are still visible for search engine crawlers and they can work and be rendered with our JavaScript but they are basically just a little bit less efficient than server components they send more JavaScript to the client they take maybe a bit longer to load but this is the price we have to pay to get in the activity Now by default all components in the next JS app router are server components and you create smaller client components whenever you need them so these client components are opt-in you have to add this use client directive to turn a component into a client component whenever you need any kind of interactivity and the idea is to create these small islands of client components to make only these parts client components but again you will see examples of this in a moment okay let's just take a look at some code and as I said before the functionality of the website hasn't changed in case you haven't seen this yet in the course we built this yeah blocking website where users can create accounts they can write blog posts we have a common system with nested comments so here I can write an answer we have this nested comment we even have pagination for these comments and the nested comments we have this block editor and development that always loads a bit slow but in production it's really fast and here we can set the post title we can upload an image to our Observer we have user profiles and so on yeah so check out the course if you want to learn how to build this but now I want to talk about the code and the changes we had to make to migrate this project to the app router so on the left we have the project with the Pages directory and on the right we have the same project after the app router migration the first thing you have to do to migrate from the pages to the app router is to create such an app folder either in the srz directory if you have one in your project or directly in the root whereas the pages router is located inside the Pages directory and again you can have both in the same project while you are migrating you can have a pages and the app directory and in the Pages directory we had this underscore app file which is basically the root layout this is where all the pages are rendered and where we can put common elements like the navbar and the photo which should be shown on all pages and also metadata that should be shown on all pages or as the fallback if we don't set more specific metadata on other pages and then we have this underscore document file where we don't really do anything this is just some next.js setup basically here we have the HTML tag and this next script and this main tag which are responsible to render our pages in the app router these two files go away and instead they are replaced by this layout.tsx file which is located directly in the app folder and we need such a root layout this root layout has the same purpose we set the metadata here just that it works a bit different we put common elements like the navbar and the photo that should be shown on all pages yeah and then we render the pages themselves in here and this is also where you put your context providers that should wrap all your pages in here we also have the HTML and the body tag so this replaces the underscore document file as well yeah and the underscore app file to that metadata we don't have this head tag here anymore which we had to put into the return Block in the pages router now instead we export this const metadata from the file but I like the syntax more because this way we don't have to clutter our return block with this section which is not related to the UI so I think this is much more clean and whenever we want to render a page under a certain URL in the new app router we have to create such a page.tsx file and as you can see I have quite a few of them because now they have to be called page.sx in the old Pages router we could either name our pages index.tsx in which case they would take the same address as the folder they are located in or we could put the name of the relative URL directly into the file name for example we could have a user.tsx and the URL would be root URL user to show this page in the app router these files always have to be called page day can't have another name and we put the URL into the folder structure so you can see we have this page DSX directly in the app folder that's the front page and to create a page under slash block for example we create a block folder and put a page.tsx in here if we need to Dynamic URL params which again in the pages router we put into the file name we now also put them into to the folder so here we have for example block and here we have this scrap brackets slug folder which creates a dynamic URL program so we can replace slack for anything and then we can receive this param on this page so here this slack param is whatever we put into the URL after slash block slash and then the slug as I already explained all components in the app router are server components by default so when we have a very simple component like this front page here which only renders some text then we don't have to do anything this is a server component by default it's completely rendered on the server and here we don't send any JavaScript to the client only the finished HTML basically of course it becomes more interesting when we actually fetch some data for example here on our blog post page where we load all the blog posts from the database or on the post page where we load a single post from the database let's take a look at this page for example this is under slash block Slash Slug this is where they explained a moment ago this is our Dynamic parameters can be different between different blog posts and the patch for this file is located in app Block Slug and then this page.tsx file in the patches router we had get static props and get server-side props now that our components are server components by default we can do something much more straightforward we can turn the component into an async function this is not possible in vanilla react but it's possible now in nextges 13 because this component will be rendered on a server this means we can also just fetch data with async await in here and then use this data to render the UI so we don't have this get static props function anymore that we need to fetch data and pass it to our component instead we do all of this directly inside the component now let's actually open the same file over here in the Pages directory so here we had Pages slash blog slash and then the slug param was directly in the file name which now is not possible anymore so get static props is replaced by simply calling a weight gets paused get post function is up here it just fetches some data inside the dry catch block you can ignore this unstable cache for now I explain what this is for in the course but this is not so important for now and in the Pages directory we had get static paths to tell next.js which param 3 you want to render at compile time this is now replaced by generate static params which has the same purpose but again this has a simpler API and a simpler return value here we had this weird return type that contains the object with this params key and yeah over here we just sent the slack inside an object or whatever your Dynamic URL program here is called and by default next.js caches these server components statically meaning that all the data for a page will be fetched when via compile the project the server component will be rendered but when a user opens the page we don't fetch the data again instead reserve them the cached page which then loads really fast because it's just HTML basically so this page here is in a blog post is statically cached so even if I now go into the database and change the title for example of this page and refresh the page we would still see the same title because the data is fetched when we compile the project now after we added a post we also want to see the updated title so we do something called on-demand revalidation we revalidate the cache and fetch the data again without having to compile the whole project again but again I explain more about this in my next JS course so static caching is the default in a server component we don't have to do anything special to activate this we don't have to make this decision between get static props and get server-side props anymore instead if we don't want to Cache this page we can export another const cult dynamic and when we set this to force minus Dynamic this page may not be aesthetically cached anymore instead it will be loaded from the server every time a user opens the page so this is the equivalent of get server side props in the old patches directory but in many situations this here is not even necessary because often next.js is intelligent about the caching behavior for example on the Block overview page we have the page number as a search param in the URL as you can see a question mark page equals and we read this value in our blog page which is the page TSX directly located in the block folder here we read the search param which contains the page number this page is also a server component as you can see it's an async function and here we fetch the blog post but since we use search params here and search params can only be known at runtime because next.js doesn't know what search param or page number the user types in there it automatically opts out of static caching and instead loads this page dynamically every time a user accesses this URL so this has the same effect as get server side props which we used on the Block page before with get some of that props we always load the latest data from the server we don't cache anything but this is now handled automatically in server components for us again by using the search params if we didn't use the search programs and we wanted to force this again we could export const Dynamic equals Force Dynamic and this forces this page to be a dynamically rendered one more thing to note here is that we use this Force Dynamic export because we used axles to fetch data in this app if you use the native fetch function instead so this normal fetch JavaScript function then there's actually a newer configuration in here in here we can set the cache and here we have different options available we can set this to a no cache or no store they effectively do the same thing they have the same purpose as this first dynamic they turn next JS to not cache whatever we Fetch with the fetch function and instead run it again the next time this page is accessed but the problem is often you don't use the fetch function you might be using something like axios or maybe you're using an orm like Prisma for example and then we don't have this fetch option here available in this case we have these so-called route segment conflicts which are these exports that change the caching behavior of this whole page but again we don't need this here because this page is automatically a dynamically rendered and I also left some explanation comments in the code you can take a look at it if you want again the link is in the video description I already explained that we can set metadata like the page title and the description with this metadata export but this only works for static metadata like our hard-coded title and description here if you need Dynamic metadata that depends on some data then you have to use another function for that for example on the Block article page we have the title of the blog post as part of the page title right but in order to know this title we have to fetch the data first and you do this in this generate metadata function here this is how you can create Dynamic metadata here we still return this metadata object in which we can set the title description and all these other kinds of meta information Fields also the open graph image for example but here we can fetch the data first and then use this to generate these texts like here where I use the blog post title and the page title the summary for the description and the featured image URL for the OG image again this all replaces the head tag that we had before to be honest the head tag was easier to use this is a bit awkward here because we have to fetch the data again but in return we don't clutter our return statements so I guess this is good now when you fetch your metadata there are still some things you have to keep in mind you have to de-duplicate these requests here so that we don't make this get postcard twice I explained this in the course and I also explained this in my next.js tutorials here on YouTube so I don't want to go into more detail about this here so definitely check out my tutorials if you want to learn more about this one more thing that's interesting to note is that the default RG image and the fav icon are now files that we put directly into the app folder and next.js will automatically pick them up so you put an PNG file into the app folder and when you call it open graph minus image when you give it this exact name next.js will automatically use it as the orgy image for this website so when we post a link on Twitter for example to this website this image here will be shown if you want to override the orgy image dynamically then you do this in generate metadata like we do here with the featured image URL let's actually take a look at this we can use this social share preview page to try this out I actually have this website deployed at the moment under coding and flowminusample.com we should see the RG image there it is and the title and everything and we should see a different one when we try out the URL for a specific blog post then we should see this title this description and this image here as the OG image so let's try out this URL and this works so yeah a lot of this stuff has been simplified but at the same time it can become more tricky sometimes because the implicit behavior is not always clear in the Pages directory everything was very explicit right we had get static props for static caching get server side props for dynamic fetching and now everything happens a bit more automatically but again when you wrap your head around this then it works really nicely one cool feature that helps you if this is when you will compile the project let's try this out instead of npm run def we run npm run build wait a short moment until this project is compiled then we get this output here where we see these icons that indicate how each page is cached so the cylinder icon here as you can see at the bottom means the page is dynamically rendered it says gets oversight props but yeah in the app directory it's now a dynamic server component and up here we can see which pages are rendered like this so the block overview page is dynamically rendered and some other Pages like the edit post page where we also always need the latest information right and these pages that have a DOT are statically rendered like a single blog post page for example but now let's also take a look at a client component so for example on the blog post page this whole page is a server component because we want to fetch the data and render this page on the server right but when I go to one of my own posts I have this edit post button here and as I explained earlier whenever you need on click Behavior you have to do this inside the client component but not only do we need on click Behavior showing or not showing this button also depends if we are locked in with the user that wrote this blog post in the first place and our user is fetched client-side as well so when I refresh the page we will first not see this edit post button and it will then pop up after the authenticated user has loaded so again this means that even though this whole page is a server component we have these client components inside it the edit post button but also the comments section for example because again here we need click handlers State input and so on which are all client-side features and as I mentioned before we want to avoid making this whole page a client component if we can avoid this because server components are more efficient so let let's take a look at the code of this page again in the old Pages directory we didn't really have to pay attention to this stuff we could just load the authenticated user in here and then decide if we want to show this edit post button or not but in a server component we can't use state or JavaScript anymore but you can create these little islands of client components for example I moved all of this logic here into this edit post button component the edit post button component has to use client directive at the top so this is now a client component and in here we load the user and if the user is not authenticated to a edit this post we return Naya so we don't return the button we don't show the button in the UI and again this way only this button itself is a client component and rest is still a server component and the same for the blog post comments action down here which again has this us client directive at the top and as I mentioned before when we put us client here then every component we use inside this client component is also automatically a client component We cannot put a server component into a client component that's important to understand we can only put Clan components into server components because all server components have to be rendered before the client components are rendered so when we use another component inside the block comment section for example the create comment box which is in a separate file this component does not need the use client directive at the top we could put it here like this would have the same effect but it's not necessary because the block comment section inside which we use the create comment box is already a client component and this triggers down to all the children nested inside it now again we can't put a server component into a client component but there is one important exception to this rule remember that our context providers have to be a client components because in here we use client-side features like state or effects but these context providers usually wrap our whole application right including all our pages does this mean that every page is now automatically a client component this would be pretty bad because then we don't have any server components in our website at all but the answer is no in this case we can put server components into our off models provide a client component because we are use this children prop to render components inside here when we use this children prop then next.js doesn't have to know in advance what exactly we put in here so it can still render all the server components it can then render our client components including the off models provider and then it can just take the already compiled server components and put them in here where the children slot is so this works what would not work would be something like this putting a server component directly in here where this would be a separate component but this could not be a zero component because again then next.js can't compile it in advance but with the children prop we can put server components in here so we can have our context providers that wrap our whole app and in here we can still put all our server component Pages without the problem but the context is only accessible in client components because again use context and all other hooks that start with the word use are client-side features what's also cool is in the new app router we can now co-locate all kinds of files together with our pages so we can put our CSS files for example in the same folders as our pages and other components as well whereas in the Pages directory I had on my CSS files in the separate style folder so I like to hear much more because now it's easier to find the related files and then there are also some other special file types that are explained in the documentation so we have layouts.tsx and page.tsx right and we have to give them these names to show a loading indicator when we load a server component we have loading.tsx so here I simply showed a spinner and you can sometimes see it when I refresh this page or navigate to another page sometimes you can't they are there it was for a short moment that was the loading indicator the loading page then we also have not found which replaces the old 404 page and then we also have an error.tsx somewhere which replaces the 500.tsx so they have the same purpose it's interesting to note that you can also have nested versions of these special files for example we could put a different error.tsx into our blog folder so we would show this only on slash blog pages and we can even put a layout in here so remember that our layout shows common elements between multiple Pages for example our navbar in our photo that we want to show on all pages we could add another layout.tsx into the app folder and then we could create a nested layout here so maybe the block should have another navbar or a sidebar that we only want to show on the Block but on all block Pages then we could create such a nested layout.tsx file but here we only have this one the root layout is the one you always need so you always need this one layout.tsx in the app folder I still have a separate components folder for reuse the components that I use in multiple different places one more thing to notice in this project we use bootstrap because I find it easier to use the entailwind for tutorial projects because we don't have to write so many classes and it's easier to get a good styling working fast now one problem with bootstrap and similar component libraries and a new app router is that these bootstrap components actually use client-side features internally they use context sometimes state but the developers of react bootstrap didn't add the use client directive to the code of these files yet which means that normally we can't use them inside a server component because then the compiler complains that we didn't add the use client directive to the top but again we don't want to make all our Pages like the front page a client component only because we use this bootstrap container components in here but an easy way to fix this is what you just saw we create our own file here I call this bootstrap.tsx we add the use client director here and then we just re-export all these components now we'll turn them into client components and again as I explained earlier whenever the children prop is used inside a client component we can put server components in there this is why we can have something like this where we have this bootstrap row which is a clan component but whenever we put inside it can still be rendered on the server because it's rendered inside this children's slot of the row or the column but yeah I explained this in more detail in my next JS course and in my next.js 13 beginner tutorial here on YouTube I also explained this bootstrap approach in more detail so check this out if you want to learn this but some features are still missing in the app browser and people often complain about this on GitHub for example in the Pages directory we had this use unsafe changes warning hook this hook would show a warning when we have some input here and we try to refresh the page then it would say Hey you have import you will lose this when you refresh the page and we also made this work when we navigated within the app via these links here the problem is we don't have these necessary router events anymore in the Pages directory so here we had this router events route changes started and we could interceptors this doesn't work anymore in the app directory it's just not there anymore so I had to find a way around us but I replaced this for something cooler I created this use auto save hook which automatically saves our input between Refreshers and between navigations and so on so now whenever I type something in here it saved every three seconds automatically to the session storage but you can also change the duration and when I refresh this the input is still there isn't this cool it's actually cooler than what we had before again the link to the GitHub code is in the video description if you want to check this out now one more thing that often trips people up in the app router is the client-side cache so even if our server component is dynamically fetched and branded like our profile information here there is another cache on the client between navigation so often people have the problem that they change some data on the server for example let's change the display name then they navigate to another page and they navigate back and then they see the old data again because this is cached on the client but as you can see here it works when we navigate back and forth because in most cases there's actually a simple solution to a disc caching problem let's take a look into the update user profile section which is a client component because we have input fields and state there and after we updated the user information we simply call router.refresh router is coming from the use router hook and this completely busts the client-side cache so all these cached pages are removed the page is reloaded and then when we navigate back and forth we load the latest data a few more things that are worth noting before we had the use router hook here which is an import from next slash router this one does not work anymore in the app directory now we have to import router from next slash navigation instead and from use router we can also not get the search programs and the path name anymore like we did before instead we now have separate hooks for them so we have used path name use search params and use router and often you have to use them together I don't know why they split them up but they probably have a good reason and API route handlers also look a bit different now so in the old Pages directory we had our one API route here to revalidate the post on demand we still have this in the app directory notice API routes have to be called route.ts or JS and again you have to put the dynamic URL params into the folder structure and not into the file name anymore the new syntax of this route handlers is better in my opinion because before we had this one Handler function and in here we had to check with an if check if it's a get request or a post request and so on and now we have the separate functions get an uppercase for example we can also export async function yeah patch or post or the other HTTP warps and here we do the same thing as before we revalidate the post after updating it so that we render it again and then cache the updated blog post on the server for future access now of course I can't cover everything about the migration in detail in this video again if you want to check out the updated course go to codinginflow.com nextjs and you will step by step learn how to migrate this whole project to the app router and keep an eye open for a future next.js videos on this channel and then I wish you a nice rest of the day happy coding take care foreign foreign
Info
Channel: Coding in Flow
Views: 5,189
Rating: undefined out of 5
Keywords: next.js app router, next.js app router vs page router, next.js app router example, next.js app router tutorial, next.js app router layout, next.js app router migration, next.js 13 migration, next js 12 to 13 migration, next js 13 migration guide, next js 13 app vs pages, next.js server components, next js server components example, next js server components context, next js 13 react server components, next js server components cache, app router, next 14, next js 14
Id: UlyoT08XBxM
Channel Id: undefined
Length: 40min 23sec (2423 seconds)
Published: Sun Sep 17 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.