Nguyên nhân: await ở ngay thân page.tsx chặn toàn bộ output — server không gửi được byte nào cho tới khi truy vấn xong.
Cách sửa: đẩy phần chậm xuống component con và bọc <Suspense>. Phần nhanh được stream ngay, phần chậm gửi bổ sung khi sẵn sàng.
tsx
// page.tsx
export default function Page() {
return (
<>
<Header />
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
</>
)
}
async function Stats() {
const data = await slowQuery() // await nằm trong ranh giới Suspense
return <StatsView data={data} />
}Điểm dễ sai: bọc <Suspense> quanh component nhưng vẫn await ở page cha — không có tác dụng. await phải nằm bên trong cây con của boundary.
loading.tsx chính là <Suspense> bọc tự động quanh cả segment. Nó phù hợp khi cả trang cần chờ; khi chỉ một phần chậm thì đặt boundary thủ công cho mịn hơn.
Lưu ý về HTTP: khi đã stream, status code và <head> đã được gửi đi rồi, nên lỗi phát sinh sau đó không đổi được status — phải xử lý bằng error.tsx.