Summary of Learning Next.js 13 App Router (Will be updated periodically as I learn it)

Right now it's not finished yet, as my goal is to learn slow and consistent. Someone said 1% progress a day is better. So, here it is.

1. Folder Structure

/app - contains all the routes, components, and logic for the application.

/app/lib - contains utility functions or data fetching functions.

/app/ui - this is where the components live.

/public - for public assets, i.e images.

2. CSS

We can use either global.css or CSS Module for customizing local-scoped styling.

clsx

It is a library that allow us to toggle class names. It's like conditional CSS, but more advanced. In my opinion, it's just like :class binding in VueJs.

3. Optimizing Fonts and Images

  • Fonts Optimization

Optimizing fonts will help us tackle the Cumulative Layout Shift, one of the metrics for Pagespeed. I think under the hood, Next.js is using Google Fonts so when we use it for our styling, we don't need to link anything. We just need to import it directly:

/app/iu/fonts.ts

import { Inter } from "next/font/google";

export const inter = Inter({ subsets: ["latin"] });

We can also add multiple fonts for styling. And we just need to add name of the fonts inside the curly brackets and import it from 'next/font/google'.

Variable Fonts

Next.js recommend using Variable Fonts. Using Variable Fonts, we don't need to specify the weight. So, we should use weight and define the value if we're not using Variable Fonts.

  • Image Optimization

<Image /> is just like tag <img/>, but for Next.js. Using <Image /> means that we need to add width and height attribute, otherwise it shows error on your browser.

The width and height do not determine the rendered size of the image file.

Remote Image from API or Online URL

We need to define URL patterns in next.config.js and use remotePatterns to define the list of URL.

4. Layout & Pages

Next.js uses file-system routing. It means that folders' name are the routes.

anangyoga.com/blog

To achieve that path, I need to create a directory name blog and create a file called page.tsx inside blog directory.

Keep in mind that page.tsx that will be rendered as a page. Next.js is using index.vue as the page.

Layout

The purpose of the layout is to share UI component across multiple page.

If page.tsx refers to a page, then layout.tsx will refer to a layout inside particular directory. It means that if inside blog directory has layout.tsx file, the layout will only be applied to the page.tsx inside the blog directory.

Layout is useful so that only partial page that is re-render on while other shared segments are preserved when client navigate to other page.

This technique is what we called Partial Rendering.

5. Navigating Between Pages

We can use tag <a /> to navigate between pages. But Next.js uses special tag called <Link /> which will take control the navigation.

The difference is, tag <a /> will fully-refresh the page when it's clicked while <Link /> is near-instant, no full refresh on the page.

6. Setting Up Your Database

Basically, it's just basic DB setup with ready-to-use data provided by Next.js. Read about Setting Up Your Database for more details.

It's better to read it up there.

7. Fetching Data

By default, Next.js is using React Server Components. Thus, they fetch data with Server Components. They allow you to query the database directly from the server without an additional API layer. We don't need to use useEffect or useState or data fetching libraries. Just do the async-await and everything's going to be fine.

Note: it executes on the server. Check the log on YOUR TERMINAL.

To Do: learn Route Handlers

  • Using SQL

For the data-fetching, we're going to write it using SQL.

Note: if we want to perform sort or limit, it would be better to create sort or limit query from database (in this case we're using SQL). We can sort or limit data from Front-end with Javascript, but it's expensive in terms of data fetching.

  • Request Waterfall

This request means that a fetch function will not be executed until the previous request is completed. It's not bad, but could impact web performance.

  • Parallel Data Fetching

In contrast to waterfall that will only work until a task is complete, Parallel Data Fetching will work at the same time. Even if there are 3 fetch functions, it will fetch in the same time.

Javascript provides Promise.all() to perform this condition.

8. Static & Dynamic Rendering

  • Static Rendering

Data fetching & rendering happens on the server at build time (when it's deployed).

Data on static rendering is serve (sometimes) on CDN, where the result can be distributed and cached. It means that the server will not load everytime there's request from client. It's also good for SEO because the content is there when the page loads.

  • Dynamic Rendering

Data fetching & rendering for each user at request time or when the client open the page. It provides real-time data when the page loads.

  • Making Dynamic Dashboard

Since by default @vercel/postgres doesn't set its own caching semantics, we can control to be static or dynamic rendering.

To make the dashboard dynamic rendering, we just need to call the Next.js API named unstable_noStore.

import { unstable_noStore as noStore } from "next/cache";

export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  noStore();

  // ...
}

9. Streaming

Streaming is like when you order at the restaurant and the waiter brings the food one by one, not one giant tray when everything is ready.

There are two ways you implement streaming in Next.js:

  1. At the page level, with the loading.tsx file.
  2. For specific components, with <Suspense>.
  • Streaming the whole page with loading.tsx

File that named loading.tsx will be used as UI skeleton or 'Loading...' to display when the page content loads.

Note: learn about Route Groups

  • Streaming Component

We can also use Streaming in a component. Based on Next.js example, we can fetch data on a particular component. Then wrap that component in a Suspense, and put a fallback attribute in the Suspense containing skeleton component.

Here's the example

<Suspense fallback={<RevenueChartSkeleton />}>
  <RevenueChart />
</Suspense>

Key Note:

  1. We can stream the whole page with loading.tsx
  2. We can stream individual component or group, just put the fetch function on the components that need it, then wrap the component in Suspense.

10. Partial Pre-rendering (Optional)

Note: This is experimental feature, it may change in the future.

  • Combining Static and Dynamic Content

Dynamic function such as noStore() or cookies() will make a route to be dynamic.

We can combine static & dynamic behaviour on a route. Like when we have social media feed, the posts would be static but the likes would be dynamic. Or e-commerce site where the product details are static, and the user's cart is dynamic.

  • How does Partial Prerendering work?

Wrapping a component in Suspense doesn't make the component itself dynamic (remember you used unstable_noStore to achieve this behavior), but rather Suspense is used as a boundary between the static and dynamic parts of your route.

The great thing about Partial Prerendering is that you don't need to change your code on Next.js/dashboard repository to use it. As long as you're using Suspense to wrap the dynamic parts of your route, Next.js will know which parts of your route are static and which are dynamic.