Điểm khác biệt lớn nhất của Next.js hiện đại: component mặc định chạy trên máy chủ (Server Component), chỉ những phần cần tương tác mới chạy trên trình duyệt (Client Component). Hiểu đúng ranh giới này giúp ứng dụng nhanh, an toàn và gọn nhẹ.
Bạn sẽ học được
- Phân biệt Server và Client Component
- Biết khi nào dùng "use client"
- Truyền dữ liệu từ server xuống client
- Tránh các lỗi thường gặp
Một số khái niệm
| Server Component (mặc định) | Client Component ("use client") | |
|---|---|---|
| Chạy ở đâu | Máy chủ (khi render) | Trình duyệt (và render sẵn lần đầu) |
| Truy cập CSDL, tệp, khóa bí mật | Được | Không |
| useState, useEffect, onClick | Không | Được |
| API trình duyệt (window, localStorage) | Không | Được |
| Kích thước JS gửi xuống | Không tăng | Tăng |
Server Component lấy dữ liệu
// src/app/sinhvien/page.tsx (Server Component)
import NutThich from './NutThich';
async function laySinhVien() {
const res = await fetch('http://localhost:4000/api/sinhvien'); // API Node.js loạt trước
return res.json();
}
export default async function TrangSinhVien() {
const { du_lieu } = await laySinhVien();
return (
<ul className="space-y-2">
{du_lieu.map((sv: { id: number; ho_ten: string }) => (
<li key={sv.id} className="flex justify-between rounded bg-white p-3 shadow-sm">
{sv.ho_ten} <NutThich />
</li>
))}
</ul>
);
}Client Component cho tương tác
// src/app/sinhvien/NutThich.tsx
'use client';
import { useState } from 'react';
export default function NutThich() {
const [thich, setThich] = useState(false);
return (
<button onClick={() => setThich(!thich)} className="text-xl">
{thich ? '❤️' : '🤍'}
</button>
);
}Quy tắc: đặt 'use client' ở lá của cây component (nút, form, bộ lọc), giữ trang và layout là Server Component.
Lỗi thường gặp
- Dùng useState trong component không có "use client" → lỗi biên dịch.
- Truyền hàm (function) từ Server sang Client qua props → không được (chỉ truyền dữ liệu tuần tự hóa được, hoặc Server Action).
- Import mã có khóa bí mật vào Client Component → lộ khóa. Dùng gói
server-onlyđể chặn. - Biến môi trường dùng ở client phải có tiền tố
NEXT_PUBLIC_.
Mẹo hay
Client Component vẫn có thể chứa Server Component nếu truyền qua children: <KhungClient><DanhSachServer /></KhungClient>.
Lưu ý
Đừng thêm "use client" vào layout gốc chỉ vì một nút bấm: toàn bộ cây bên dưới sẽ thành client, mất lợi ích của Server Component.
Thực hành
- Hiển thị danh sách sinh viên bằng Server Component.
- Tạo ô tìm kiếm (Client) lọc danh sách.
- Tạo nút chuyển chế độ tối lưu vào localStorage.
- Thử import fs vào Client Component và đọc thông báo lỗi.
Tóm tắt bài học
- Mặc định là Server Component.
- "use client" cho state, sự kiện, API trình duyệt.
- Đặt "use client" ở lá, truyền dữ liệu qua props.
- Bảo vệ mã bí mật bằng server-only.