typescriptintermediate

Next.js Streaming with Suspense

Stream server components with Suspense boundaries for progressive page loading and better TTFB.

typescript
// app/dashboard/page.tsx
import { Suspense } from 'react';

export default function Dashboard() {
  return (
    <div className="space-y-4">
      <h1>Dashboard</h1>

      {/* Fast content renders immediately */}
      <WelcomeCard />

      {/* Slow content streams in with loading fallback */}
      <Suspense fallback={<AnalyticsSkeleton />}>
        <Analytics />
      </Suspense>

      <Suspense fallback={<RecentOrdersSkeleton />}>
        <RecentOrders />
      </Suspense>
    </div>
  );
}

function WelcomeCard() {
  return <div className="p-4 bg-blue-50 rounded-lg">Welcome back!</div>;
}

// Slow async server component — streams when ready
async function Analytics() {
  const res = await fetch('https://api.example.com/analytics', {
    cache: 'no-store',
  });
  const data = await res.json();
  return (
    <div className="grid grid-cols-3 gap-4">
      <StatCard label="Users" value={data.users} />
      <StatCard label="Revenue" value={`$${data.revenue}`} />
      <StatCard label="Orders" value={data.orders} />
    </div>
  );
}

async function RecentOrders() {
  const res = await fetch('https://api.example.com/orders?limit=5');
  const orders = await res.json();
  return (
    <ul>
      {orders.map((o: { id: string; amount: number }) => (
        <li key={o.id}>Order #{o.id} — ${o.amount}</li>
      ))}
    </ul>
  );
}

function AnalyticsSkeleton() {
  return <div className="h-24 bg-gray-200 animate-pulse rounded-lg" />;
}

function RecentOrdersSkeleton() {
  return <div className="h-40 bg-gray-200 animate-pulse rounded-lg" />;
}

function StatCard({ label, value }: { label: string; value: string | number }) {
  return (
    <div className="p-4 bg-white rounded-lg shadow">
      <p className="text-sm text-gray-500">{label}</p>
      <p className="text-2xl font-bold">{value}</p>
    </div>
  );
}

Use Cases

  • Progressive loading for data-heavy dashboards
  • Improving perceived performance with streaming
  • Independent loading states for page sections

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.