What is Nextjs

Next.js is a full-stack SSR framework based on React, providing a series of tools for automatic configuration of React,
allowing us to focus on building applications and simplify the configuration process.

What is SSR(Server Side Render)

SPA (Single Page App)

When it comes to server-side rendering, we have to mention SPA (Single Page Applications). Examples of single page
applications include React and Vue, which operate by downloading JavaScript to the client side for execution.
They undergo compilation and building processes to create JavaScript virtual DOM objects, which are then mounted onto
index.html all at once. Since they are mounted on a single HTML file, for instance:

The SPA application provides an index.html file, which contains a div tag with a specified id.
The SPA application provides an index.html file, which contains a div tag with a specified id.
Then, in the entry JS of the SPA, the program is rendered and mounted onto the specified div.
Then, in the entry JS of the SPA, the program is rendered and mounted onto the specified div.

Other modifications are based on it, so it is a single-page application.
Moreover, SPA programs place the JavaScript files for rendering and logical processing on the client side, where the
rendering tasks are handled by the client, hence they are also known as CSR (Client-Side Rendering) programs.

On the other hand, server-side rendering involves the server rendering the HTML file and then delivering it to the
client, returning the corresponding HTML file based on different requests.

Comparison

  • Performance
    CSR assigns the rendering task to the client, which can lead to lag on devices with poor performance, but it
    relatively reduces the server’s load. In contrast, SSR faces significant stress during peak request times due to the
    need to handle a large volume of requests, but it alleviates pressure on the client side.
  • SEO-friendliness
    Before rendering, it is difficult for CSR to obtain specific information about the webpage, making it not
    SEO-friendly. However, SSR can provide the rendered files to search engine crawlers, at which point the page content
    is essentially complete, facilitating analysis and recognition by the engine algorithms.
  • Initial load time
    CSR requires downloading the JS to the client for rendering, so there might be a period of blankness on the page (an
    issue with initial screen rendering) before the JS is downloaded to the client.
    On the other hand, SSR renders the HTML on the server first, eliminating this step and reducing client-side burden,
    thus it can display content to users more quickly.

Setup Nextjs Project

git repository

The teaching content of this article will be placed in the GitHub repository, which can be studied in conjunction.
Different branches will correspond to different chapters.

init project

1
2
3
4
5
npx create-next-app@latest next-course
# or
pnpm dlx create-next-app@latest next-course
# or
pnpm create next-app

Alternatively, you can use my open-source project to create.

Function 1: File Routing System

In version 13, Next.js introduced a new App Router built on React Server Components, which supports shared layouts,
nested routes, loading states, error handling, and more.

Next.js automatically recognizes files in the app directory with the extension .page.jsx or .page.tsx to create
pages, while their parent or sibling files called layout.jsx or layout.tsx are used as page layouts for rendering.

try it

Assuming we want to create a blog website and the access path for the blog is site.example/blog/test/,
let’s create the directory file structure accordingly:

1
2
3
4
5
6
- app
+ blog
+ test
+ page.tsx
- page.tsx
- layout.tsx
jsx
1
2
3
4
5
6
7
8
9
// app/blog/test/page.tsx

export default function TestBlogPage() {
return (
<div>
Hi, there is a blog page!
</div>
)
}

Add a link to it in app/page.tsx.

jsx
1
2
3
4
5
6
7
8
9
10
11
// app/page.tsx

export default function Home() {
return (
<div>
<a href={'/blog/test'}>
Go to test blog
</a>
</div>
);
}

start it:

1
2
3
npm run dev
# or
pnpm dev
Due to the default CSS styles, it looks a bit odd.
Due to the default CSS styles, it looks a bit odd.

Modify the content in globals.css to make the background look nicer.

1
2
3
4
5
:root {
/*--foreground-rgb: 0, 0, 0;*/
/*--background-start-rgb: 214, 219, 220;*/
/*--background-end-rgb: 255, 255, 255;*/
}

result:

home page
home page
click `Go to test blog`
click `Go to test blog`

Ok, You completed the challenge!

Route mapped to URL

From the example we just looked at, we can see that the mapping relationship from file to route is as follows:

file://site.dir/app/a/b/page.jsx -> site.example/a/b

Dynamic Route

You might have noticed that the current URLs are hardcoded.
If we need 10 different blog pages (such as blog/test2, blog/abc, blog/start), using this method would require
manually creating 10 different xxx/page.js/jsx/ts/tsx files, which is very time-consuming and inefficient.

Of course, Next.js provides a solution called dynamic routes.

The file route mapping for it looks like this:

file://site.dir/app/blog/[slug]/page.jsx -> site.example/blog/a, site.example/blog/b, site.example/blog/c

try it

Modify our directory structure.

1
2
3
4
- app
- blog
+ [slug]
+ page.tsx

Create a blog content page.

jsx
1
2
3
4
5
6
7
8
9
10
// app/blog/[slug]/page.tsx

export default function BlogPage() {
return (
<div>
{/*生成随机数*/}
blog {Math.round(Math.random() * 10)}
</div>
)
}
Visiting `blog/a` reached our `[slug]/page`.
Visiting `blog/a` reached our `[slug]/page`.
If you have kept the `blog/test/page` from before, you'll find it still works.
If you have kept the `blog/test/page` from before, you'll find it still works.

layout

Next.js provides a layout file that offers layout functionality, which is UI shared among multiple routes. When
navigating, the layout retains its state, remains interactive, and does not re-render. Layouts can also be nested.

Let’s analyze the layout file:

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import type {Metadata} from "next";
import {Inter} from "next/font/google";
import "./globals.css";

// This is a font, don't worry about it for now.
const inter = Inter({subsets: ["latin"]});

// Provides metadata for the page.
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
{/*"children" refers to the content inside the subdirectory (including the layout and page of the subdirectory).*/}
{children}
</body>
</html>
);
}

Let’s add a navbar and a footbar.

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// components/footbar.tsx

export default function Footbar() {
return (
<footer className={'bg-gray-200 border-b border-b-gray-300 border-t border-t-gray-300 sticky h-20 w-full'}>
footbar
</footer>
)
}

// components/navbar.tsx

export default function Navbar() {
return (
<nav className={'bg-green-200 border-b border-b-gray-300 sticky w-full h-20'}>
navbar
</nav>
)
}

Have you not learned Tailwind CSS yet? Check out this article (todo).

add them to layout.tsx

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// app/layout.tsx
///
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Navbar/>
{children}
<Footbar/>
</body>
</html>
);
}

Result:

provide blog contents

OK, you now have dynamic routes that can match different names, as well as having created a navbar and footbar. However,
we want the page to display more content.

write markdown

Many note-taking applications now support Markdown syntax, which is a method I particularly favor. Additionally, there
are numerous npm libraries available that can parse the contents of Markdown files into HTML rich text strings.

learn markdown Syntax?

let us change directory structure

1
2
3
4
5
6
- app
- ...
- contents
- mountain.md
- bird.md
- flower.md
mountain.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
![](https://cdn.pixabay.com/photo/2022/10/24/12/20/mountains-7543273_1280.jpg)

# Title: Summit Reflections: A Mountain's Mirror to the Soul

Climbing mountains is more than a physical endeavor; it's a journey of the spirit, where each peak reveals a facet of
inner strength. As I scaled the rugged trails, the grind of my boots against rock echoed the resilience required to
surmount life's challenges. The ascent, steep and demanding, taught me endurance. With each step, I found a metaphor for
the effort needed to achieve one's dreams.

The view from the summit was not just a vista of vast landscapes but a perspective on the infinite possibilities life
offers. It reminded me that after every great effort comes a broad expanse of opportunity. The descent, often
overlooked, was no less instructive. It spoke of humility and caution; a reminder that what goes up must come down with
grace.

This mountain experience distilled life into a simple yet profound truth: the journey matters as much as the
destination. It's in the climb that we discover our mettle and in the view that we savor our triumphs.

(_create by AI_)
flower.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
![](https://cdn.pixabay.com/photo/2023/03/19/05/31/flower-7861942_960_720.jpg)

# Title: Blossoming Insights: A Whiff of Flowers

As I meandered through the garden, the air was thick with the sweet perfume of blooming flowers. Each petal, a tender
brushstroke on nature's canvas, painted a picture of grace and resilience. The flowers, in their silent language, spoke
of beauty that survives amidst the harshest conditions.

A delicate rose, its petals softer than silk, nodded gently in the breeze. It reminded me that even the most stunning
forms can emerge from thorny paths. In the blossoms, I saw a reflection of life's inherent beauty and the fortitude to
flourish despite challenges.

The garden, with its kaleidoscope of colors, became a sanctuary where every flower told a story of transformation and
growth. My spirits were lifted by this quiet symphony of scents and hues, a testament to nature's power to inspire and
replenish the soul.

(_create by AI_)
bird.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
![](https://cdn.pixabay.com/photo/2024/01/19/18/08/bird-8519547_1280.jpg)

# Title: Capturing the Charm of Feathered Friends

Today's venture into the serene woods was a delightful encounter with nature's delicate treasures. As I wandered through
the dappled sunlight under the canopy, my camera was my faithful companion, ready to freeze moments in time.

A soft trill caught my attention, leading me to a vibrant avian presence. A tiny bird, with feathers arrayed in hues of
blue and green, perched gracefully on a branch. It seemed almost aware of its own charm, bobbing and turning, as if
posing for an unseen audience.

I snapped a sequence of shots, each click capturing a different angle of this natural splendor. The bird, in its
innocence, carried on with its song, unaware of the beauty it bestowed upon my day.

As I left the woods, my heart felt lighter, and my camera held a piece of joy that I will cherish. These moments of
connection with nature are what truly nourish the soul.

(_create by AI_)

read and show

Install the dependencies required to parse Markdown.

1
2
3
npm i marked
# or
pnpm i marked

Read and parse files in the code.

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app/blog/[slug]/page.tsx

import {readFile} from "node:fs/promises";
import {marked} from "marked";

export default async function BlogPage({
params,
searchParams,
}: {
params: { slug: string }
searchParams: {}
}) {
const text = await readFile(`./contents/${params.slug}.md`, 'utf8')
const html = marked(text)

return (
<div>
<div dangerouslySetInnerHTML={{__html: html}}></div>
</div>
)
}

Great, we have now successfully parsed and rendered the Markdown text onto the page!

Result:

Address the issue with text styling.

You might have noticed that our heading styles are the same as the regular text. This is because Tailwind CSS removes
default CSS styles, but we can use a library to address this issue.

1
2
3
npm i -save-dev @tailwindcss/typography
# or
pnpm i -save-dev @tailwindcss/typography

Register as a Tailwind plugin.

1
2
3
4
5
6
7
8
// tailwind.config.ts
const config: Config = {
/// ...
plugins: [
require('@tailwindcss/typography')
],
};
export default config;

Then use it to add the ‘prose’ class name to components.

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app/blog/[slug]/page.tsx

import {readFile} from "node:fs/promises";
import {marked} from "marked";

export default async function BlogPage({
params,
searchParams,
}: {
params: { slug: string } // 接收url参数: (/blog/[slug] -> slug)
searchParams: {}
}) {
const text = await readFile(`./contents/${params.slug}.md`, 'utf8')
const html = marked(text)

return (
// flex flex-row justify-center -> 内容居中
<div className={'w-screen flex flex-row justify-center'}>
{/* prose让文本中的标题有对应的样式 */}
<div className={'prose'} dangerouslySetInnerHTML={{__html: html}}></div>
</div>
)
}

Restart the server and access the page again.

Function 2: Provide API interfaces.

Next.js will parse the files in the api directory as backend interfaces, receiving and processing HTTP requests.

We will move the operation of reading and parsing Markdown files to the backend (api directory), while keeping the
rendering work on the frontend (app directory).

try it

Change our directory structure.

1
2
3
4
5
- app
+ api
+ blogs
+ [slug]
+ route.ts

The current correspondence between the file system and URLs is as follows:

1
app/api/blogs/[slug]/route.ts -> site.example.com/api/blogs/a, site.example.com/api/blogs/b, ...

Write code to handle requests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// api/blogs/route.ts

import {NextRequest, NextResponse} from "next/server";
import {readFile} from "node:fs/promises";
import {marked} from "marked";

// 接收GET请求
export async function GET(req: NextRequest) {
// 解析url
let slug = req.url.slice(
req.url.lastIndexOf('/') + 1,
req.url.length
)

let html
try {
// 读取md文件
const text = await readFile(`./contents/${slug}.md`, 'utf8')
html = marked(text)
} catch (err) {
console.error(err)
// 错误返回
return NextResponse.json({error: err})
}

// 返回html内容
return NextResponse.json({html})
}

// 接收POST请求
export async function POST(req: NextRequest) {
return NextResponse.json({})
}

Result:

Access a non-existent md file.
Access a non-existent md file.
Access a file that normally exists.
Access a file that normally exists.

Request backend data from the front end.

jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// app/blog/[slug]/page.tsx

// 服务端组件可以使用async
export default async function BlogPage({
params,
searchParams,
}: {
params: { slug: string } // 接收url参数: (/blog/[slug] -> slug)
searchParams: {}
}) {
// 请求后端数据
let res = await fetch(`http://localhost:3000/api/blogs/${params.slug}`, {
method: "GET"
})
let json = await res.json()
let html
if (json.error)
html = "Ooh! Something went wrong"
else
html = json.html

return (
// flex flex-row justify-center -> 内容居中
<div className={'w-screen flex flex-row justify-center'}>
{/* prose让文本中的标题有对应的样式 */}
<div className={'prose'} dangerouslySetInnerHTML={{__html: html}}></div>
</div>
)
}

Result:

success
success
Access a non-existent md file.
Access a non-existent md file.

At this point, we will pause here. To prevent the article from becoming too lengthy, I have placed the remaining content
in separate sections, which you can click below to continue reading.

Next Step

Use next-themes for day and night theme switching.

Have you seen websites that can switch between light and dark themes? We can also achieve this.

Connect to the database using Prisma.

Storing data in a file system or cache is one option, but using a database to store data is a more common choice.

Use Next-Auth for authentication.

Authentication is a common feature in applications. Using Next-Auth eliminates the hassle of creating your own login and
registration pages, allowing you to focus on implementing authentication logic.

Use TipTap as a rich text editor.

TipTap is a modern headless rich text editor that allows us to easily create attractive page content.

How to customize TipTap through code? Check out my latest article (todo).


本站总访问量