Fetch dữ liệu trong Server Component
Next.js mặc định mọi component đều là Server Component — tức là chúng chạy trên server, không phải trên trình duyệt. Nhờ đó, bạn có thể kết nối thẳng vào database và query dữ liệu ngay bên trong component, không cần tạo API route trung gian.async:
Lưu ý: Nếu bạn cần lấy dữ liệu từ API bên ngoài (third-party), vẫn có thể dùngfetch()trong Server Component — không cầnuseEffect.
Chỉ dùnguseEffectkhi component đó là Client Component ('use client').
Vậy khi nào thực sự bắt buộc phải dùng useEffect trong Client Component?
1. Cần tương tác với browser APINhững thứ chỉ tồn tại trong trình duyệt —
localStorage, sessionStorage, navigator, window, document. Server không có những thứ này, nên không thể chạy ở Server Component.
Server Component chỉ chạy một lần khi render. Nếu bạn cần re-fetch dựa trên input của người dùng — tìm kiếm live-search, infinite scroll, autocomplete — thì phải ở client.
WebSocket, SSE (Server-Sent Events), hoặc các subscription như Firebase/Supabase realtime — những thứ cần kết nối liên tục trong suốt vòng đời component.
Nhiều thư viện JS (chart, map, rich text editor…) cần DOM node thực sự mới khởi tạo được — không thể chạy trên server.
| Column 1 | Column 2 |
|---|---|
| Tình huống | Dùng gì |
| Fetch data lúc load trang | Server Component (+ Suspense nếu cần) |
| Fetch lại khi user thao tác | useEffect trong Client Component |
Cần window, localStorage… | useEffect trong Client Component |
| Real-time / WebSocket | useEffect trong Client Component |
| Thư viện cần DOM | useEffect trong Client Component |
useEffect là để phản ứng với những thứ xảy ra sau đó trên trình duyệt.
Hiển thị loading với loading.tsx
Next.js có một file reserved là loading.tsx. Đặt nó cùng thư mục với page.tsx, Next sẽ tự động hiển thị component này trong khi trang đang chờ dữ liệu.
Dùng <Suspense> để loading đúng chỗ
Giải pháp tốt hơn là tách phần cần dữ liệu thành một component riêng, rồi bọc nó bằng <Suspense>:
<h1> hiển thị ngay lập tức, chỉ có danh sách bên dưới mới chờ — đúng behavior mà người dùng mong đợi.
Xử lý lỗi với error.tsx
Tương tự, Next.js có file reserved error.tsx để bắt lỗi xảy ra trong quá trình render:
- Phải thêm
'use client'ở đầu file — đây là yêu cầu bắt buộc của Next.js vì error boundary hoạt động ở phía client. - Component nhận vào prop
error(để hiển thị thông điệp lỗi) vàreset(để thử render lại).
Xử lý 404 với not-found.tsx
Khi một trang hoặc resource không tồn tại, dùng file not-found.tsx để tạo template 404:
notFound() khi route hợp lệ nhưng data không tồn tại — thường gặp nhất ở dynamic routes, import từ next/navigation:
not-found.tsx có thể đặt ở nhiều cấp khác nhau:
not-found.tsx gần nhất với nơi gọi notFound().
Lưu ý thực tế (production)
loading.tsx vs <Suspense>Dùng
loading.tsx cho các trang có toàn bộ nội dung phụ thuộc vào data (trang dashboard, trang danh sách). Dùng <Suspense> khi trang có cả phần tĩnh lẫn phần động — đây là pattern được khuyến nghị nhiều hơn vì UX tốt hơn.
error.tsx nên có nút “Thử lại”Prop
reset cho phép re-render lại segment hiện tại mà không cần reload toàn trang. Đây là chi tiết nhỏ nhưng cải thiện UX đáng kể, đừng bỏ qua.
Không log thông tin nhạy cảm ra error.messageỞ production,
error.message đôi khi có thể chứa thông tin nội bộ (tên bảng, connection string…). Nên log đầy đủ ở server (dùng console.error hoặc dịch vụ như Sentry), còn hiển thị ra UI chỉ nên là thông điệp chung chung.
Scope not-found.tsx cẩn thậnĐặt
not-found.tsx ở app/ sẽ override trang 404 mặc định của Next — hãy đảm bảo nó đủ thông tin, có link về trang chủ, và không quá “trống”.
Tóm tắt
| Column 1 | Column 2 | Column 3 |
|---|---|---|
| File | Mục đích | Lưu ý |
loading.tsx | Hiển thị skeleton/spinner khi trang đang load | Ẩn toàn bộ trang — cân nhắc dùng <Suspense> thay thế |
<Suspense> | Loading có chọn lọc theo từng component | Linh hoạt hơn loading.tsx, ưu tiên dùng |
error.tsx | Bắt lỗi runtime trong route segment | Bắt buộc 'use client' |
not-found.tsx | Hiển thị trang 404 | Dùng cùng notFound() từ next/navigation |