Có thể bạn chưa biết về Next.js (v13+)

7 min read
-- views
-- likes
-- comments
Có thể bạn chưa biết về Next.js (v13+)

Series: Next.js

Episodes: (1/3)

Cho đến thời điểm bài viết này được publish thì version mới nhất của Next.js đang là 14.1.0, tuy nhiên nhìn chung thì version 14 chỉ là một bản cập nhật mang lại sự tối ưu cũng như ổn định cho version 13 chứ không có nhiều khái niệm mới.

Ở trong bài viết này, mình sẽ cùng tìm hiểu về một số thứ hay ho mà có thể bạn đã bỏ lỡ trong Next.js (dĩ nhiên là từ Next.js 13+) nhé. Let's go.

1. Route group

Hãy nhìn vào cây thư mục sau:

Bạn có thấy các folder đang được sắp xếp rất lộn xộn ? Next.js sử dụng file-system base router, tức là các route được định nghĩa dựa vào các file và folder. Sắp xếp folder một cách lộn xộn khiến cho việc tìm kiếm một route cụ thể trở nên khó khăn hơn, đó là lý do mà Route Groups ra đời. Đúng như ý nghĩa tên gọi của nó, Route Groups giúp bạn nhóm các route có cùng chung một logic mà không làm ảnh hưởng đến cấu trúc đường dẫn URL.

Dưới đây là cách tái cấu trúc thư mục trên bằng cách sử dụng Route Groups.

Những route như /forgot-password, sign-in, sign-up được nhóm thành group (auth), còn lại được nhóm vào group (dashboard), clear hơn nhiều chứ nhỉ ?

Vầ tất nhiên là Route Group sẽ không thêm tên thư mục vào URL.

URL: http:localhost:3000/sign-in

2. Static Metadata

Next.js bao gồm sẵn Metadata API giúp bạn định nghĩa metadata cho ứng dụng (ví dụ thẻ meta và thẻ link bên trong thẻ head của file HTML) để cải thiện SEO và khả năng chia sẻ ứng dụng. Bạn có thể sử dụng Metadata API ở trong cả page.tsx hoặc layout.tsx như thế này:

import type { Metadata } from "next"
 
export const metadata: Metadata = {
  title: "TaiTD Blog",
  description: "Blog created by Trịnh Đình Tài",
}

3. Dynamic Metadata

Trong ứng dụng, có những route sẽ sử dụng Metadata động, ví dụ như mỗi bài viết cụ thể hoặc là mỗi một sản phẩm bất kỳ trên trang web sẽ có metadata khác nhau. Về cơ bản có thể hiểu là Dynamic Route thì sẽ có Dynamic Metadata.

Trong trường hợp này, chúng ta cần sử dụng hàm generateMetadata để fetch metadata động cho route, ví dụ như sau:

import type { Metadata } from "next"
 
type Props = {
  params: {
    id: string
  }
}
 
export const generateMetadata = ({ params }: Props): Metadata => {
  return {
    title: `Product ${params.id}`,
  }
}
 
export default function FavouriteProductDetails({ params }: Props) {
  return <h1>Favouraite Product Details {params.id}</h1>
}

Tiêu đề của trang được thay đổi dựa vào id sản phẩm.

4. Private Routes (Private Folder)

Private Folder có thể được tạo bằng cách thêm tiền tố _ vào thư mục folderName.

Khi đó, hệ thống định tuyến sẽ coi như thư mục này và tất cả các thư mục con của nó không tham gia vào routing.

An example folder structure using private folders

Private folder nên được sử dụng khi nào ?

  • Tách UI logic khỏi routing logic.
  • Sắp xếp nhất quán các tệp nội bộ trong toàn bộ dự án Next.js
  • Sắp xếp và nhóm các tập tin trong code editor.
  • Tránh xung đột đặt tên với các quy ước tệp Next.js trong tương lai.

5. Catch-all Segments

Trong Next.js, "segment" thường được dùng để chỉ "một phần" hoặc là "một phân đoạn" của một URL. Ví dụ, trong URL https://example.com/posts/first-post, có thể coi "posts" và "first-post" là các "segment" của URL.

Dynamic Segments có thể được mở rộng để bắt tất cả các subsequent segments bằng cách thêm dấu ... bên trong segmentName như sau: [...segmentName]

Ví dụ: /docs/[...slug]/page.tsx sẽ khớp với /docs/topic , nhưng cũng khớp với /docs/topic/10 , hoặc là /docs/topic/10/section/1, tuy nhiên không khớp với /docs

Dưới đây là đoạn code ví dụ:

app/docs/[...slug]/page.tsx
import React from "react"
 
type Params = {
  params: {
    slug: string[]
  }
}
 
export default function SlugPage({ params: { slug } }: Params) {
  return (
    <div>
      <h1>Viewing Custom Slug Page</h1>
      <span>URL Contains: {slug.toString()} </span>
    </div>
  )
}

Liệu chúng ta có thể sửa được lỗi 404của route /docs không ? Cùng tiếp tục tới phần tiếp theo nhé.

6. Optional Catch-All Segments

Catch-all Segments có thể được đặt thành tùy chọn bằng cách đưa tham số vào dấu ngoặc vuông kép: [[...segmentName]].

Ví dụ: /docs/[[...slug]]/page.tsx cũng sẽ khớp với /docs, ngoài /docs/topic, /docs/topic/10.

Bạn có một navigation bar với các menu link, làm cách nào để user biết được mình đang view trang nào ở trên màn hình ?

Việc highlight active link giúp cho trải nghiệm người dùng tốt hơn rất nhiều, dưới đây là cách giúp bạn làm được điều đó.

Tạo file navbar.tsx trong thư mục components, đây là client component - do có tính tương tác với user thông qua menu item, hãy thêm use client ở đầu file.

"use client"
 
import React from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
 
type Links = {
  title: string
  url: string
}
 
export default function Navbar() {
  const links: Links[] = [
    {
      title: "Sign In",
      url: "/sign-in",
    },
    {
      title: "Favourite",
      url: "/favourite/1",
    },
  ]
 
  const pathname = usePathname()
 
  return (
    <div className="flex space-x-4">
      {links.map(({ title, url }: Links) => {
        const isActive = pathname.startsWith(url)
 
        return (
          <Link
            className={isActive ? "font-bold text-emerald-500" : "text-white"}
            key={title}
            href={url}
          >
            {title}
          </Link>
        )
      })}
    </div>
  )
}

Okey, ở đoạn code trên, mình đã tạo ra một navigation bar chứa 2 link có path là /sign-in/favourite, UI của nó như sau:

Click link và xem nó có gì thay đổi so với các link còn lại:

Và trên đây là một vài khái niệm trong Next.js 13+ mà có thể bạn đã bỏ lỡ 👌
Nếu thấy bài viết hay, hãy cho mình một like nhé ✅