Nguyễn Đình Cường
0 comment
09 Nov, 2026
Next.js là framework xây dựng trên React, giúp bạn lo sẵn nhiều thứ mà dự án thật cần: định tuyến, render phía máy chủ, tối ưu hình ảnh, triển khai. Với App Router, cách tổ chức dự án xoay quanh thư mục: cấu trúc thư mục chính là cấu trúc đường dẫn của website.
npx create-next-app@latest my-app
cd my-app
npm run devTrình tạo sẽ hỏi bạn về TypeScript, Tailwind, ESLint và việc dùng App Router. Các tùy chọn mặc định thay đổi theo phiên bản nên hãy đọc kỹ các câu hỏi. Mã nguồn trang nằm trong thư mục app.
Mỗi thư mục bên trong app là một đoạn đường dẫn. Một thư mục chỉ trở thành trang công khai khi có file page.js (hoặc .tsx) bên trong:
app/
page.js → /
gioi-thieu/
page.js → /gioi-thieu
blog/
page.js → /blog
[slug]/
page.js → /blog/bat-ky-slug-naoThư mục đặt tên trong ngoặc vuông như [slug] là route động. Giá trị trên URL được truyền vào component:
// app/blog/[slug]/page.js
export default async function BaiViet({ params }) {
const { slug } = await params; // ở các phiên bản mới, params là một promise
return <h1>Bài viết: {slug}</h1>;
}Cách nhận params có thay đổi giữa các phiên bản, nên hãy đối chiếu tài liệu của phiên bản bạn cài. Điều cốt lõi không đổi: tên thư mục quyết định đường dẫn.
File layout.js bao bọc các trang con và giữ nguyên khi chuyển trang trong cùng nhánh, nên không bị tải lại. Layout gốc app/layout.js là bắt buộc và chứa thẻ html và body:
// app/layout.js
export const metadata = {
title: 'Học lập trình',
description: 'Website chia sẻ kiến thức lập trình',
};
export default function RootLayout({ children }) {
return (
<html lang='vi'>
<body>
<header>Menu chính</header>
{children}
<footer>Chân trang</footer>
</body>
</html>
);
}Đối tượng metadata thiết lập title và description cho SEO một cách khai báo. Bạn cũng có thể thêm layout riêng cho từng nhánh, ví dụ app/blog/layout.js để thêm thanh bên cho khu blog.
loading.js: giao diện hiển thị trong lúc trang đang tải dữ liệu.error.js: giao diện khi có lỗi trong nhánh đó.not-found.js: giao diện cho trang 404.route.js: định nghĩa API route (xử lý GET, POST...) ngay trong dự án.import Link from 'next/link';
<Link href='/blog'>Xem blog</Link>Thành phần Link chuyển trang ở phía trình duyệt và tải trước (prefetch) các trang liên kết, giúp điều hướng gần như tức thì. Dùng nó thay cho thẻ a với liên kết nội bộ.
Đây là khái niệm quan trọng nhất của App Router. Theo mặc định, mọi component là Server Component:
async/await, và giữ bí mật như khóa API trên máy chủ.Khi cần tương tác, bạn thêm chỉ thị 'use client' ở đầu file để biến nó thành Client Component:
'use client';
import { useState } from 'react';
export default function NutThich() {
const [thich, setThich] = useState(0);
return <button onClick={() => setThich(thich + 1)}>Thích ({thich})</button>;
}Nguyên tắc thực dụng: giữ phần lớn trang là Server Component, chỉ tách những "hòn đảo" cần tương tác thành Client Component nhỏ, đặt càng sâu trong cây càng tốt. Server Component có thể import và render Client Component, nhưng không làm ngược lại theo cách trực tiếp.
// app/blog/page.js
async function layBaiViet() {
const res = await fetch('https://api.example.com/posts');
if (!res.ok) throw new Error('Không tải được bài viết');
return res.json();
}
export default async function Blog() {
const bai = await layBaiViet();
return (
<ul>
{bai.map(b => (
<li key={b.id}><Link href={'/blog/' + b.slug}>{b.tieuDe}</Link></li>
))}
</ul>
);
}Không cần useEffect hay state cho việc tải dữ liệu ban đầu, vì component chạy trên máy chủ. Nếu thiếu import Link, hãy thêm import Link from 'next/link' ở đầu file.
Khi dự án lớn, bạn muốn nhóm file mà không làm thay đổi đường dẫn. Thư mục đặt trong ngoặc tròn, như (marketing), là route group: tên không xuất hiện trên URL nhưng cho phép mỗi nhóm có layout riêng. Thư mục bắt đầu bằng dấu gạch dưới, như _components, được loại khỏi định tuyến nên an toàn để chứa file dùng chung. Những quy ước này giữ cho cây thư mục gọn gàng khi website có hàng chục trang.
App Router biến thư mục thành đường dẫn, layout.js thành khung dùng chung, và chia component thành hai loại: Server mặc định, Client khi cần tương tác. Hãy thử tạo một blog nhỏ với trang danh sách và trang chi tiết bằng route động để cảm nhận.
Khi bạn muốn đi từ ví dụ nhỏ này đến một website hoàn chỉnh đã triển khai, hãy xem các khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment