Khi xây dựng bất kỳ ứng dụng web nào, việc điều hướng giữa các trang là một trong những chức năng cốt lõi nhất. Trong thế giới của Next.js, "navigation" và "linking" không chỉ đơn giản là việc chuyển trang - đó là một hệ thống được tối ưu hóa mạnh mẽ để mang lại trải nghiệm người dùng nhanh như chớp và thân thiện với SEO.
Bài viết này sẽ giúp bạn làm chủ hoàn toàn hệ thống định tuyến (routing) của Next.js, từ những khái niệm cơ bản nhất với component <Link>
đến các kỹ thuật nâng cao sử dụng hook useRouter
. Dù bạn là người mới bắt đầu hay đã có kinh nghiệm, hãy cùng khám phá nhé!
🥇 Nguyên tắc vàng: Client-Side Navigation
Trước khi đi vào chi tiết, bạn cần nắm vững khái niệm quan trọng nhất: Client-Side Navigation.
Trong các trang web truyền thống, mỗi khi bạn nhấp vào một liên kết, trình duyệt sẽ gửi một yêu cầu đến máy chủ, tải lại toàn bộ trang HTML, CSS và JavaScript. Quá trình này gây ra một khoảng trễ và cảm giác "khựng" (flicker) trắng xóa trên màn hình.
Next.js giải quyết vấn đề này một cách thông minh. Khi bạn sử dụng các công cụ điều hướng của nó:
- Trang ban đầu được render từ server (Server-Side Rendering - SSR) để tải nhanh và tốt cho SEO.
- Sau đó, ứng dụng React (Next.js) sẽ "tiếp quản" (hydrate) trên trình duyệt.
- Mọi lần điều hướng tiếp theo sẽ diễn ra ở phía client. Next.js chỉ tải về những đoạn mã JavaScript cần thiết cho trang mới và thay đổi nội dung trên DOM mà không cần tải lại toàn bộ trang.
Kết quả? Một trải nghiệm người dùng mượt mà, tức thì, không có cảm giác gián đoạn, giống hệt như bạn đang dùng một ứng dụng di động (native app).
🔗 Nền tảng của mọi liên kết: Component <Link>
Công cụ cơ bản và mạnh mẽ nhất để tạo điều hướng trong Next.js chính là component <Link>
. Thay vì sử dụng thẻ <a>
thông thường, bạn sẽ bọc nó trong <Link>
.
Cú pháp cơ bản
Cách sử dụng đơn giản nhất là truyền một chuỗi đường dẫn vào prop href
.
import Link from 'next/link'
function Navbar() {
return (
<nav>
<Link href="/">Trang Chủ</Link>
<Link href="/about">Giới Thiệu</Link>
<Link href="/blog">Blog</Link>
</nav>
)
}
Tại sao không dùng <a>
trực tiếp?
Khi bạn dùng <Link>
, Next.js sẽ tự động xử lý mọi thứ:
- Client-Side Navigation: Kích hoạt cơ chế điều hướng phía client.
- Prefetching (Tải trước): Đây là một tính năng cực kỳ lợi hại! Khi một component
<Link>
xuất hiện trong tầm nhìn của người dùng (viewport), Next.js sẽ tự động tải trước mã của trang đó trong nền. Khi người dùng thực sự nhấp vào liên kết, trang đã sẵn sàng để hiển thị ngay lập tức. Đây là một trong những bí quyết giúp ứng dụng Next.js nhanh đến vậy.
Điều hướng đến các trang động (Dynamic Routes)
Hầu hết các ứng dụng đều có các trang động, ví dụ: /blog/[slug]
, /products/[id]
. Việc liên kết đến chúng cũng rất đơn giản.
import Link from 'next/link'
function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
{/* Sử dụng template literal để tạo đường dẫn động */}
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}
Bạn cũng có thể truyền một object vào href
để có thêm nhiều tùy chọn, ví dụ như truyền query parameters:
<Link
href={{
pathname: '/shop/search',
query: { category: 'electronics', sort: 'price_asc' },
}}
>
Tìm đồ điện tử giá tăng dần
</Link>
// Tương đương với: /shop/search?category=electronics&sort=price_asc
⚡ Điều hướng linh hoạt: Hook useRouter
và usePathname
Không phải lúc nào việc điều hướng cũng xuất phát từ một cú nhấp chuột vào liên kết. Đôi khi bạn cần chuyển trang sau khi người dùng thực hiện một hành động, chẳng hạn như gửi một form hoặc hoàn thành một giao dịch. Đây là lúc các hook vào cuộc.
Lưu ý quan trọng: Cú pháp của các hook này có sự khác biệt nhỏ giữa App Router (Next.js 13+) và Pages Router (phiên bản cũ hơn).
Dành cho App Router (Next.js 13+)
Trong App Router, bạn sẽ import các hook từ next/navigation
.
useRouter()
: Cung cấp các phương thức để điều hướng chương trình.usePathname()
: Trả về đường dẫn hiện tại của URL.useSearchParams()
: Cho phép bạn đọc các tham số query của URL.
Ví dụ thực tế: Chuyển hướng sau khi đăng nhập
'use client' // Bắt buộc phải có vì đây là component sử dụng hook
import { useRouter } from 'next/navigation'
export default function LoginForm() {
const router = useRouter()
const handleSubmit = async (event) => {
event.preventDefault()
// Logic xác thực người dùng...
const isAuthenticated = true // Giả sử đăng nhập thành công
if (isAuthenticated) {
// Đẩy người dùng đến trang dashboard
router.push('/dashboard')
}
}
return (
<form onSubmit={handleSubmit}>
{/* Các trường input... */}
<button type="submit">Đăng Nhập</button>
</form>
)
}
Các phương thức hữu ích của router
:
router.push('/path')
: Đẩy một route mới vào stack lịch sử. Người dùng có thể bấm nút "Back" của trình duyệt để quay lại.router.replace('/path')
: Thay thế route hiện tại. Người dùng không thể "Back" lại trang cũ (hữu ích cho trang đăng nhập/đăng ký).router.refresh()
: Tải lại dữ liệu mới nhất cho route hiện tại từ server mà không làm mất state phía client.router.back()
: Quay lại trang trước đó trong lịch sử.router.forward()
: Đi tới trang tiếp theo trong lịch sử.
✨ Các kỹ thuật nâng cao và Mẹo hay
1. Đánh dấu liên kết đang hoạt động (Active Link)
Một yêu cầu phổ biến là thay đổi style cho liên kết tương ứng với trang người dùng đang xem. Hook usePathname
là công cụ hoàn hảo cho việc này.
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import './styles.css' // Giả sử có file CSS với class 'active'
export default function NavLink({ href, children }) {
const pathname = usePathname()
const isActive = pathname === href
return (
<Link href={href} className={isActive ? 'active' : ''}>
{children}
</Link>
)
}
2. Vô hiệu hóa Prefetching
Trong một số trường hợp, bạn có thể không muốn tải trước một trang (ví dụ: trang có dữ liệu lớn và ít được truy cập). Bạn có thể tắt tính năng này đi.
<Link href="/heavy-page" prefetch={false}>
Trang Nặng
</Link>
3. Kiểm soát việc cuộn trang (Scroll Restoration)
Mặc định, Next.js sẽ cuộn lên đầu trang mới khi bạn điều hướng. Bạn có thể thay đổi hành vi này bằng prop scroll
.
// Khi nhấp vào, trang sẽ không cuộn lên đầu
<Link href="/#section-three" scroll={false}>
Đi đến Section 3
</Link>
Dành cho Pages Router (Next.js phiên bản cũ)
Nếu bạn đang làm việc với một dự án cũ hơn sử dụng Pages Router, các khái niệm tương tự nhưng hook được import từ next/router
.
import { useRouter } from 'next/router'
function MyComponent() {
const router = useRouter()
// Lấy đường dẫn: router.pathname
// Lấy query: router.query
// Điều hướng: router.push('/path')
// Ví dụ lấy slug từ URL động /blog/[slug]
const { slug } = router.query
return <p>Đây là bài viết: {slug}</p>
}
Kết luận: Làm chủ Navigation và Linking không khó
Trước khi kết thúc, hãy tóm tắt lại các nội dung quan trọng trong bài viết với bảng so sánh nhanh dưới đây:
Tính năng | Component <Link> | Hook useRouter (App Router) |
---|---|---|
Mục đích | Tạo liên kết điều hướng do người dùng khởi tạo (nhấp chuột). | Điều hướng theo chương trình (sau một sự kiện, logic). |
Cách dùng | <Link href="/path">...</Link> | const router = useRouter(); router.push('/path'); |
Prefetching | Bật mặc định. | Không áp dụng. |
SEO | Rất tốt, các crawlers có thể đọc được thẻ <a> bên trong. | Không trực tiếp ảnh hưởng đến SEO. |
Khi nào dùng | Thanh điều hướng, danh sách bài viết, liên kết nội bộ. | Sau khi gửi form, đăng nhập/đăng xuất, các hành động logic. |
Hệ thống navigation và linking trong Next.js là một sự kết hợp hoàn hảo giữa sự đơn giản và sức mạnh. Bằng cách tận dụng Client-Side Navigation, component <Link>
với khả năng prefetching thông minh và sự linh hoạt của hook useRouter
, bạn có thể xây dựng những ứng dụng web với trải nghiệm người dùng mượt mà, nhanh chóng và liền mạch.
Hiểu rõ và áp dụng đúng các công cụ này không chỉ giúp cải thiện hiệu năng ứng dụng mà còn là chìa khóa để tạo ra những sản phẩm chuyên nghiệp, chinh phục được những người dùng khó tính nhất.
Chúc bạn thành công trên hành trình làm chủ Next.js!