Stream Slow Data in Next.js Instead of Blocking the Route

Flags slow awaits that hold back an entire Next.js layout or page, and Suspense boundaries placed where they cannot stream anything.

What it flags

Flaggedapp/dashboard/page.tsx
import { getAccount, getRecommendations } from '@/lib/data';
import { AccountSummary } from './account-summary';
import { Recommendations } from './recommendations';

export default async function DashboardPage() {
  const account = await getAccount();
  // No loading.tsx or Suspense: a slow third-party call holds back the page
  const recommendations = await getRecommendations(account.id);

  return (
    <main>
      <AccountSummary account={account} />
      <Recommendations items={recommendations} />
    </main>
  );
}
Passesapp/dashboard/page.tsx
import { Suspense } from 'react';
import { getAccount, getRecommendations } from '@/lib/data';
import { AccountSummary } from './account-summary';
import { Recommendations, RecommendationsSkeleton } from './recommendations';

export default async function DashboardPage() {
  const account = await getAccount();

  return (
    <main>
      <AccountSummary account={account} />
      <Suspense fallback={<RecommendationsSkeleton />}>
        <RecommendationsSection accountId={account.id} />
      </Suspense>
    </main>
  );
}

async function RecommendationsSection({ accountId }: { accountId: string }) {
  const items = await getRecommendations(accountId);
  return <Recommendations items={items} />;
}

Why it matters

Without a Suspense boundary, a Server Component route sends no HTML until every await in it resolves, so one slow recommendations query or third-party call sets the time to first byte for the whole page. A slow await in a layout is worse: it delays every page beneath it, and the same segment's loading.tsx does not cover it because the loading boundary sits inside the layout. The page still renders correctly, so the regression shows up as a blank screen on first load or a link that seems to do nothing, never as a failing test.

Agent instructions

cubic checks every pull request against these instructions. Paste them into AGENTS.md or Cursor rules and your AI coding tool follows them while it writes code.

AGENTS.md

Add this section to AGENTS.md at the repository root. Codex, Cursor, and other AI coding tools read it.

## Stream Slow Data in Next.js Instead of Blocking the Route

Applies to `**/*.tsx`.

Review Next.js layouts, pages, and async Server Components for slow data that holds back the whole response instead of streaming behind a Suspense boundary.

Flag changes that:
- Await slow or secondary data (recommendations, activity feeds, analytics, third-party APIs, large aggregates) at the top of a page before returning any JSX, when neither a loading.tsx nor a Suspense boundary covers it.
- Await request-time data at the top of a layout.tsx; it delays every page under that layout, and the same segment's loading.tsx does not cover the layout itself.
- Await data in a parent and pass the result into a component wrapped in <Suspense>, so the boundary sits below the await and its fallback never shows; move the await into the wrapped component, or pass the unawaited promise to a Client Component that reads it with use().
- Put a fast, critical request and a slow, secondary one in the same top-level Promise.all, so the critical content waits for the slowest call.
- Place several independent slow sections behind one shared Suspense boundary, so each waits for the slowest instead of streaming in as it resolves.
- Remove a loading.tsx or Suspense boundary that covered slow data without replacing it.

Do not flag:
- Fast awaits the page cannot render without, such as the session, the primary record, or a check that calls notFound() or redirect(); keeping these before any Suspense boundary preserves a real HTTP status.
- Data that is cached or prerendered at build or revalidation time instead of fetched per request.
- Pages where the slow data is the primary content and a loading.tsx already shows a meaningful skeleton.

Checks files matching **/*.tsx.

More Next.js agents

See all Next.js code review standards

Run this agent on every pull request

cubic reviews every pull request against the agents you turn on and comments on the lines that break them.

Catch this in your code