typescriptadvanced

Partial Prerendering with Suspense

Combine static shells with streamed dynamic content using React Suspense for instant page loads.

typescript
import { Suspense } from 'react';

// Static shell — prerendered at build time
export default function ProductPage({ params }: { params: { id: string } }) {
  return (
    <div className="max-w-4xl mx-auto p-8">
      <h1 className="text-3xl font-bold">Product Details</h1>

      {/* Dynamic — streamed on request */}
      <Suspense fallback={<PriceSkeleton />}>
        <DynamicPrice productId={params.id} />
      </Suspense>

      <Suspense fallback={<ReviewsSkeleton />}>
        <DynamicReviews productId={params.id} />
      </Suspense>
    </div>
  );
}

async function DynamicPrice({ productId }: { productId: string }) {
  const price = await fetchPrice(productId); // dynamic
  return <p className="text-2xl">${price.amount}</p>;
}

function PriceSkeleton() {
  return <div className="h-8 w-24 bg-gray-200 rounded animate-pulse" />;
}
function ReviewsSkeleton() {
  return <div className="h-32 bg-gray-200 rounded animate-pulse" />;
}
async function DynamicReviews({ productId }: { productId: string }) {
  return <p>Reviews for {productId}</p>;
}
async function fetchPrice(_id: string) {
  return { amount: 29.99 };
}

Use Cases

  • E-commerce product pages
  • Dashboard layouts
  • Personalized content

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.