Next.js 13.4: Everything You Need to Know (Part - 1)
Building Dynamic and SEO-Friendly Web Apps Made Easy
Next.js is a React framework that enables you to build fast, dynamic, and SEO-friendly web apps with ease. It provides features such as file-system-based routing, server-side rendering, automatic code splitting, data fetching, image optimization, and more.
This will be a series of 3 blogs on Next.js 13.4
In this blog series, I will introduce you to some of the new features and improvements that Next.js 13.4 brings to the table. These include:
App Router (Stable): A new way of defining routes and layouts using the app directory (Part 1)
Server Components: A new feature that allows you to render components on the server and stream them to the client (Part 1)
Writing APIs using Next.js (Part 2)
Turbopack (Beta): A new Rust-based bundler that improves the performance and stability of your local development server (Part 2)
Server Actions (Alpha): A new feature that allows you to mutate data on the server with zero client JavaScript (Part 2)
Part 3 will be a go-through of a full-stack project using Next.js!
Letβs dive into each of these features and see how they can enhance your Next.js development experience.
Getting started with Next.js 13.4
To get the boiler-plate code for the latest version of Next.js, open the terminal and ship the code using -
npx create-next-app@latest
Now you will get a few options to configure, such as pre-installing Tailwind CSS with the boilerplate, using the \src
directory, using \app
the directory (which you should definitely use), and a few more. Here is what I chose π
Next.js 13.4 fundamentals
App Router
The App Router is a new feature that replaces the pages directory with the app directory. It allows you to define routes and layouts using the file system and supports features such as React Server Components, nested routes, streaming, suspense, and built-in SEO support. One thing to note here is that Next.js 13.4 still supports the pages directory for a hassle-free incremental upgrade from previous Next versions to Next.js 13.4.
The App Router is designed to be dynamic without limits. You can create complex interfaces that share UI between routes, preserve state across navigations, avoid expensive re-renders, and enable advanced routing patterns.
Every folder in the app directory is a new route in the app. So, for example, if you want to create a login route, there will be a folder named login
.
Every folder must contain a file named page.tsx
or page.jsx
(case-sensitive). It is a special file provided by Next.js which creates the unique UI of a route and makes the path publicly accessible. The core UI logic and calling of the components generally take place here. So, add the following code in /app/login/page.tsx
:-
import React from "react";
const LoginPage = () => {
return <div>Hello I am in the login page.</div>;
};
export default LoginPage;
Often, when your app grows, you might feel the need to make folders for personal organization but avoid making them a part of a route. For example, if you created a login, signup, profile settings, and profile view and don't want to clutter the pp directory with these 4 folders, you can name a folder in round brackets (some_folder_name)
and Next.js will automatically ignore it. So, you can make a folder, (auth)
and keep login
and `signup` settings" and "profile folder named (profile)
and keep /profile-settings and /profile-view in it.
You can also create subdirectories inside the app to define nested routes. For example in /app/blog/page.tsx
:
import React from "react";
const BlogPage = () => {
return <div>Blog page.</div>;
};
export default BlogPage;
and in /app/blog/[slug]/page.tsx
import React, { FC } from "react";
type Props = {
params: {
slug: string;
};
};
const BlogSlugPage: FC<Props> = ({ params }) => {
return <div>{params.slug}</div>;
};
export default BlogSlugPage;
There's a lot to talk about on the App router but for now, let's now move on to Server components
and then come back to the app router.
Server components
Server Components allow developers to better leverage server infrastructure. For example, large dependencies that previously would impact the JavaScript bundle size on the client can instead remain entirely on the server, leading to improved performance. As a result, the server components generally load faster than the client components.
Try console logging some stuff in the above pages we created and you will be surprised to see that those logs don't appear in the browser console, but they rather appear in your terminal where Next.js app is running.
Do try it out as it will form the base of this section!
By default, Next.js now creates server components
and to create a client component
you need to add use client;
directive as the first line of your applications.
Let's do some code to fetch data from an API and show it on a page using both server and client components. In this way, it would be way easier to explain and understand!
Using the default server components.
An important point to note here is that you can not use any react-hooks in a server component as hooks were made for the client components. Well, not only you but also the Next.js devs too felt bad knowing that you can no more handle the loading state or errors state using useState and useEffect. But then they introduced some crazy solutions to handle it. They are:
Asynchronous pages - From now on, the pages can be asynchronous, so, you can now directly fetch the data from an API and store it in a variable. No useState and useEffect are needed! In the following code, in
/app/blog/[slug]/page.tsx
, I will be making an API call. Now this will be an asynchronous page and hence until we get back the data from the API call, the page won't load :) Well, Next.js has provided a solution for this as well!import React from "react"; import axios from "axios"; type Props = { params: { slug: string; }; }; const delayLoad = (ms: number) => new Promise((res) => setTimeout(res, ms)); // To simulate loading state const BlogSlugPage = async ({ params }: Props) => { await delayLoad(3000); // delaying the API call by 3000 milli-seconds and hence triggering loading.tsx for 3000 milli-seconds const { data } = await axios.get( "https://official-joke-api.appspot.com/random_joke" ); return ( <div> {params.slug} API data = {JSON.stringify(data)} </div> ); }; export default BlogSlugPage;
loading.tsx page - The special file
loading.tsx
helps you create meaningful Loading UI. So, while your asynchronous page.tsx is loading, the loading.tsx will automatically load and stay until thepage.tsx
(the page that is making API call) is ready to load.import React from "react"; const loading = () => { return <div>Loading page</div>; }; export default loading;
Now, if you followed my code till here, after starting/restarting your next app, you will see
loading page
for 3 seconds and then you will see the data that you get from the API call (in my case a random joke). The output will be as follows πerror.tsx page - The
error.tsx
file convention allows you to gracefully handle runtime errors in nested routes. Next.js gives 2 special props to this component:Error
reset()
So to print the error message you can use the error
prop and to reset the API call, you can use the reset()
function. The code will be as follows:
"use client";
import React from "react";
type Props = {
error: Error;
reset: () => void;
};
const error = ({ error, reset }: Props) => {
return (
<div>
<h1>Something went wrong</h1>
<pre>{error.message}</pre>
<button onClick={reset}>Reset</button>
</div>
);
};
export default error;
It is important to note here that the error component has to be a client component. That is why I have used the use client
directive here. So suppose we replace axios.get("url")
by axios.post("url")
, we will encounter a 404 error as below π
On clicking the Reset button, the API call will be made once again.
It is important to note here that, although, I haven't styled these pages/components, you can definetly beautify page.tsx, loading.tsx, or the error.tsx.
In fact, you can make a folder named
components
inside the app directory to store reusable components and call them on a page well.
Using client components
To do the same stuff as above using client components does not involve any Next.js-specific concept apart from using the "use client";
directive at the top of the page (as in error.tsx). A pseudo-code for this is:-
1. Using useState to set the temporary state of apiData.
2. Using useEffect to fetch the data and setting the data to apiData
3. if apiData is null -> render <Loading /> else render stringified apiData
The app directory finally will look somewhat like this π
Summary
Next.js is a React framework that allows for building fast, dynamic, and SEO-friendly web apps with features like server-side rendering, automatic code splitting, data fetching, image optimization, and more.
The
App Router
is a new feature that replaces the pages directory with the/app
directory, allowing for dynamic and complex interfaces that support advanced routing patterns and nested routes.Each folder in the app directory is a new route and must contain a file named page.tsx or page.jsx for creating the unique UI of a route and making the path publicly accessible.
Server Components
allow for better leveraging of server infrastructure, leading to improved performance by keeping large dependencies on the server.Asynchronous pages, loading.tsx, and error.tsx files are special solutions introduced by Next.js for handling loading and error states in server components without using react hooks.
The error component must be a client component.
A folder named components inside the app directory can be used for storing reusable components and calling them on a page.