Agent Skills - Phần 2: Next.js Best Practices
Tài liệu tổng hợp Next.js Best Practices dùng làm nguồn tham chiếu cho Agent Skills (ví dụ trong Cursor), bao gồm các chủ đề: Routing, Rendering, Data Fetching, Images, Fonts, Metadata, API, Middleware, Environment, Performance và Security. Mỗi guideline được đánh giá mức độ quan trọng và có ví dụ code cụ thể. Nội dung dựa trên bộ guideline từ ui-ux-pro-max-skill, giúp developers và AI Agents xây dựng ứng dụng Next.js hiệu quả và tuân thủ best practices.
Tài liệu này tổng hợp Next.js Best Practices dùng làm nguồn tham chiếu cho Agent Skills (ví dụ trong Cursor).
Nội dung dựa trên bộ guideline từ ui-ux-pro-max-skill — file nextjs.csv.
Cập nhật: 08/02/2026.
Mức độ: 🔴 Cao · 🟡 Trung bình · 🟢 Thấp
Ví dụ code: + nên · - tránh
1. Routing
1.1 🟡 Use App Router for new projects
App Router (app/) là cách tiếp cận được khuyến nghị cho Next.js 14+.
- Nên: Dùng
app/directory vớipage.tsxcho route mới.
+ app/dashboard/page.tsx
- Không nên: Bắt đầu project mới với
pages/.
- pages/dashboard.tsx
1.2 🟡 Use file-based routing
Routing nên hoàn toàn dựa trên cấu trúc file trong app/.
- Nên: Dùng
page.tsxcho route vàlayout.tsxcho layout.
+ app/blog/[slug]/page.tsx
- Không nên: Tự cấu hình router thủ công.
- Custom router setup
1.3 🟢 Colocate related files
Colocate component, styles, tests ngay cạnh route để dễ bảo trì.
- Nên: Đặt component liên quan cạnh
page.tsx.
+ app/dashboard/_components/
- Không nên: Một
components/dashboard/chung cho mọi nơi.
- components/dashboard/
1.4 🟢 Use route groups for organization
Route groups giúp tổ chức folder mà không ảnh hưởng URL.
- Nên: Dùng dấu ngoặc
()cho route groups.
+ (marketing)/about/page.tsx
- Không nên: Tạo folder thật làm thay đổi URL.
- marketing/about/page.tsx
1.5 🟡 Handle loading states with loading.tsx
Loading UI nên tách riêng bằng loading.tsx cho từng route.
- Nên: Tạo
loading.tsxcạnhpage.tsx.
+ app/dashboard/loading.tsx
- Không nên: Dùng
useStateloading trong chính page cho mọi thứ.
- useState for loading in page
1.6 🔴 Handle errors with error.tsx
Error nên được bắt ở cấp route bằng error.tsx.
- Nên: Tạo
error.tsxvới hàmresetđể retry.
+ app/dashboard/error.tsx
- Không nên:
try/catchlặp lại trong mọi component.
- try/catch in page component
2. Rendering
2.1 🔴 Use Server Components by default
Server Components giúp giảm lượng JS gửi xuống client.
- Nên: Giữ component là Server Component trừ khi cần interactivity.
+ export default function Page() {}
- Không nên: Thêm
'use client'khi không cần.
- ('use client') for static content
2.2 🔴 Mark Client Components explicitly
Component cần hooks / event handler phải là Client Component có 'use client'.
- Nên: Thêm
'use client'ở đầu file khi dùnguseState,onClick, ...
+ 'use client'
+ // component với useState / events
- Không nên: Dùng hooks trong Server Component hoặc quên directive.
- No directive with useState
2.3 🔴 Push Client Components down
Giữ Client Component ở leaf node để tối thiểu hóa JS phía client.
- Nên: Bọc phần interactive trong client wrapper riêng.
+ <InteractiveButton /> // client leaf
- Không nên: Đánh dấu cả
page.tsxlà Client Component khi không cần.
- ('use client') on page.tsx
Ranh giới 'use client' càng nằm sâu thì càng ít JS phải gửi xuống trình duyệt:
2.4 🟡 Use streaming with Suspense
Streaming giúp render sớm phần nhanh, trì hoãn phần chậm.
- Nên: Dùng
Suspensecho phần fetch chậm.
+ <Suspense>
+ <SlowComponent />
+ </Suspense>
- Không nên: Chờ tất cả data xong rồi mới render.
- await allData; // rồi mới render
2.5 🟡 Choose the correct rendering strategy
Chọn giữa SSG, SSR, ISR tùy đặc tính dữ liệu.
- Nên: Dùng
generateStaticParams+revalidatehợp lý.
+ export const revalidate = 3600;
- Không nên: Dùng SSR cho nội dung hoàn toàn tĩnh hoặc fetch không cấu hình cache.
- fetch(url) // không cấu hình cache
3. Data Fetching
3.1 🔴 Fetch data in Server Components
Data ban đầu nên fetch trong Server Component async, không phải useEffect.
- Nên: Fetch trực tiếp trong
async function Page().
+ async function Page() {
+ const data = await fetch(url);
+ // ...
+ }
- Không nên: Dùng
useEffectđể fetch initial data.
- useEffect(() => fetch(url), []);
3.2 🔴 Configure caching explicitly (Next.js 15+)
Trong Next.js 15+, fetch mặc định là uncached — cần cấu hình cache rõ ràng.
- Nên: Chỉ định
cache: 'force-cache'cho dữ liệu tĩnh.
+ fetch(url, { cache: 'force-cache' });
- Không nên: Giả định
fetch(url)mặc định được cache.
- fetch(url) // uncached trong v15
3.3 🟢 Deduplicate fetch requests
Next.js tự dedupe các fetch giống nhau – không cần cache layer thủ công trong đa số case.
- Nên: Gọi cùng một
fetchtrong nhiều component khi cần.
+ const data = await fetch(url);
- Không nên: Tự xây cache trùng lặp khi không cần thiết.
- Custom cache layer cho cùng URL
3.4 🟡 Use Server Actions for mutations
Server Actions là cách chuẩn để xử lý form/mutation trong App Router.
- Nên: Dùng
action={serverAction}trên form.
+ <form action={createPost}>
+ ...
+ </form>
- Không nên: Tạo API Route cho mọi mutation nếu chỉ dùng nội bộ.
- <form onSubmit={callApiRoute}>
3.5 🟡 Revalidate data appropriately
Sau mutation, cần revalidate route hoặc tag để UI luôn mới.
- Nên: Dùng
revalidatePath/revalidateTagtrong Server Action.
+ await createPost();
+ revalidatePath('/posts');
- Không nên: Ở client dùng
router.refresh()ở khắp nơi để “fix”.
- router.refresh() ở mọi chỗ
4. Images
4.1 🔴 Use next/image for optimization
next/image tối ưu hóa ảnh (lazy load, resize, format) tự động.
- Nên: Dùng
<Image>cho ảnh trong app.
+ <Image src={url} alt="..." width={400} height={300} />
- Không nên: Dùng
<img>trực tiếp cho ảnh trong UI.
- <img src={url} />
4.2 🔴 Provide width and height
Thiếu kích thước ảnh gây layout shift.
- Nên: Luôn cung cấp
width/heighthoặcfill.
+ <Image src={url} alt="..." width={400} height={300} />
- Không nên: Bỏ trống kích thước.
- <Image src={url} alt="..." />
4.3 🟡 Use fill for responsive images
Ảnh full-bleed nên dùng fill + parent có position: relative.
- Nên: Dùng
fill+object-covercho ảnh phủ container.
+ <Image fill className="object-cover" />
- Không nên: Hard-code width/height bằng
window.width...
- <Image width={window.width} />
4.4 🔴 Configure remote image domains
Ảnh remote phải được whitelist trong next.config.js.
- Nên: Cấu hình
remotePatternscụ thể.
+ images: {
+ remotePatterns: [{ hostname: 'cdn.example.com' }],
+ }
- Không nên: Cho phép
'*'hoặc cấu hình quá rộng.
- domains: ['*']
4.5 🟡 Use priority for LCP images
Ảnh hero trên màn hình đầu tiên nên gắn priority.
- Nên: Dùng
prioritycho ảnh LCP.
+ <Image priority src={hero} alt="Hero" />
- Không nên: Gắn
prioritycho mọi ảnh.
- <Image priority ... /> // cho mọi ảnh
5. Fonts
5.1 🟡 Use next/font for fonts
next/font giúp self-host font và tránh layout shift.
- Nên: Dùng
next/font/googlehoặcnext/font/local.
+ import { Inter } from 'next/font/google';
- Không nên: Nhúng link Google Fonts trực tiếp.
- <link href="https://fonts.googleapis.com/..." />
5.2 🟢 Apply font to layout
Font nên được áp dụng ở root layout để đồng nhất.
- Nên: Set
classNamecho<body>tronglayout.tsx.
+ <body className={inter.className}>
- Không nên: Import font riêng ở từng page.
- Mỗi page tự import font
5.3 🟢 Use variable fonts
Variable font giảm số file & kích thước bundle.
- Nên: Dùng 1 variable font cho nhiều weight.
+ Inter({ subsets: ['latin'] })
- Không nên: Import từng weight thành nhiều file riêng.
- Inter_400, Inter_500, Inter_700
6. Metadata
6.1 🟡 Use generateMetadata for dynamic metadata
Metadata động nên được generate từ generateMetadata.
- Nên: Dùng
generateMetadata({ params })cho page động.
+ export async function generateMetadata({ params }) {
+ return { title: params.slug };
+ }
- Không nên: Hard-code metadata khắp nơi.
- export const metadata = {};
📖 Metadata
6.2 🟡 Include OpenGraph images
OG image giúp sharing social đẹp & thống nhất.
- Nên: Cấu hình Open Graph images.
+ export const metadata = {
+ openGraph: { images: ['/og.png'] },
+ };
- Không nên: Bỏ trống OG image cho các trang quan trọng.
- No OG configuration
6.3 🟡 Use metadata API instead of manual head tags
Dùng metadata/generateMetadata thay vì tự viết <head>.
- Nên: Export
metadataobject.
+ export const metadata = { title: 'Page' };
- Không nên: Viết thẳng
<head><title>...</title></head>.
- <head><title>Page</title></head>