The latest updates to Next.js are a game-changer. If you have built a new app using Next.js, you will understand what I mean. In this article, I will also be discussing some issues with the pages router. As you may know, we can create multiple pages using the pages directory. This may seem overwhelming at first, but I will be breaking it down further.
A look at the topics to discuss in detail:
- The AppRouter; this is the main part,
- Then there are the Server Actions, and
- The Turbopack.
The last two are in the alpha version. This article will mostly focus on the AppRouter, in which we will be talking about
- React Server Components
- Data Fetching
- Routes and Layouts
Let us first try to understand server-side rendering.
What is Server-side Rendering?
But this is very time-consuming and not very performant as the whole execution of the task that we wrote for our components is getting executed on the browser.
React Server Components
Why React Server Components?
Have you heard of the waterfall model?
Let us look at this code:
Suppose I have three components – a parent component, a child component, and inside that, I have a child component. This is a three-level parent-and-child relationship. There will be three components, first, second, and third. I want my second component to be rendered only when the first component has been rendered. And I want my leaf component to be rendered only when the second component has been rendered.
In my parent component, I will execute my async action. In
useEffect, I will call my API. Then if loading is true, it will show loading. And if loading is not false, it will show the child component. Similarly, the child will also do this. It will get its own data; if loading is true, it will show loading is true. And if loading is not false, it will show its child component. We can see that the third component, the child component, is dependent on the second component, and the second component is dependent on the parent component. Only when the parent component is rendered the other components will be rendered. This is called a waterfall model.
What can we do to improve this? What if I could tell all my parent-child components that you can request for data only in your component? You do not need to depend on the other components. You call your API in your function. This can be done through React Server Components. In React Server Components, we can call our API. We do not need any hooks like useEffect. We can call our API inside its server component. So my parent component can get its data in its component. The child will get its data. They will not be dependent on the other components. This is another problem that React Server Component has solved.
Render Only Required Components on Client
The advantages of this are many:
- Massive savings in bundle size
- Initial page load will be faster
- SEO will be improved (better than Next 12)
- Components are server by default
Next.js has introduced a new routing. It is an entirely different architecture because it enables React Server Components. You must have heard of React Streaming or React Suspense in the latest React 18 version. The new routing also enables us to use these. Below, you can see a dashboard layout.
Every page that you can create in Next.js will have a dashboard layout. Root layout is the layout for all your pages, and dashboard layout will be the layout for your individual page. So if I create a dashboard, it will have a dashboard layout. If I create an admin page, then it can have an admin layout. We can separate every page with its own layout. And there will be one root layout, which will be sitting on the top of the app. And we can do anything here. We can put HTML body and HTML tags here as well. The features of routing can be broken down as below:
- Route Groups: You can separate your routes from each other. You can have different layouts in them.
- Dynamic Routes: In V12, we had ..slug file. This is almost similar to that.
- Parallel Routes: if I have a layout, a page where I want to show two different pages, for example, an analytics page and a dashboard page, I can combine those two pages into one single page. These two pages would have their own loading states, error states, etc.
- Intercepting Routes: On Instagram, you can see a list of feeds on your first page. If you click on a post, you go back to another page. Intercepting routes ensure that if I press a photo of someone, a new modal will appear, and that person's page can appear. So I can load a different page altogether into the existing page that I have.
- Middlewares: You can run your code before the request is over. You can redirect it and change the headers of your requests.
Simplified Data Fetching
No more getStaticProps or getServerSideProps. In v12, we used to write different functions, such as
getServiceSideProps, to get all the data. Then we used to pass that to our component, and it was a lot of heavy stuff that we used to do. Now, things have changed. We can now write our own async functions inside our component itself.
I Tweeted last year about one of the problems with Next.js that I faced.
Suppose I have different pages for showing my data- like the homepage, dashboard page and other pages. I used to have a header on every page. In Next.js 12, we used to call our
getServiceSideProps functions. We used to get our data and pass that to the header component every time because the header was the persistent component. It sits on top of every page.
We wrote our async functions on every page to show data in the header component. And then we used to pass that to the header. But that meant writing a lot of code for us. And the bundle size would also increase. The new layouts RFC solves the problem of passing my data to a header component every time. Through React Server Components, I do not need every page to send my data to the header component. I can call my async function inside only the header component. And that would execute every time.
Async and Await in Server Components
I tried an experiment a few days back using the Prisma ORM to get data. I created a Users table in Prisma. And then, I got that data inside the component itself.
In the image above, you can see that I just called
new PrismaClient. I initiated the client. I wrote
users = await prisma. user. findMany(). This is an SQL query. It is running an SQL query inside the React component. Instead of writing multiple APIs, we need two lines to get our data. We can use async and await in our server components.
Combined SSG, SSR, and ISR
It is similar to SSR. Anytime I reload, I will get all my data again and again. The final one is similar to ISR cached with a lifetime of 10 seconds. The data will not be executed on the server whenever I reload my app. It will be executed after every 10 seconds. This is a definite advantage as we used to write whole functions inside our React components in Next.js 12, but now that has changed. Also, in Next.js 12, we could not combine these two. If I wanted some async function to be prefetched on every request or cached with a lifetime of 10 seconds, I did not have that option in Next.js 12. Now in v13, I can have three all three running simultaneously.
Server Actions is still in alpha. This enables us to mutate data on the server, calling functions directly without creating an in-between API layer. Earlier, when I wanted to create a user on my database, I had to create an API, send that form data to my API, then it would pass that data and send it to the backend, and then the backend would do all those stuff of inserting in database. But that is not the case anymore. This has:
- Enhanced user experience with features like optimistic updates and progressive enhancement (experimental for now). I can make my changes on the client, and I can tell my client that this has been done, but I send my request to the server. The server will tell after some time whether it was a success or failure. Until then, I can tell my client that this was a success. So after the request comes, I can optimistically check whether the response was a success.
- Enhanced developer experience by simplifying offloading data updates to the server, which would’ve required an API in the past. As seen below, I have created a
createUserfunction inside my component and added a directive
‘use server’. I initialize the Prisma Client and I create a User. I get the form data from the JSX I have written and can directly put my data on the database without needing the API layers.
Automatic Code Splitting
- 700x faster updates than Webpack
- 10x faster updates than Vide
- 4x faster cold starts than Webpack
- Fast and flexible developer experience for apps of any size
I was trying to experiment a few days ago with Prisma and Drizzle. Drizzle is a new ORM with type safety. In the video shared below, you can see how I created a railway app that allows me to add users to the database.
In conclusion, the release of Next.js 13.4 brings exciting new features and improvements that enhance the development experience and performance of Next.js applications. It will be interesting to find out how all these new features will work together for building better, upgraded apps.
For the complete code and talk, watch video below 🔽