Next.js 15 PPR for SEO Landing Pages — KEL IT
Websites 5 min read

Next.js 15 Partial Prerendering: SEO Landing Pages That Stay Interactive

Landing pages face a familiar tension: Google wants fast, fully-rendered HTML, while users expect calculators, booking forms, and animated hero sections. Pure SSR adds latency on every request. Static generation can’t personalize or stream live data. Client-side hydration delivers speed but hurts INP and leaves crawlers staring at empty shells until JavaScript runs.

Partial Prerendering (PPR) in Next.js 15 splits the difference. A static HTML shell ships instantly from the edge; dynamic “holes” fill in via streaming. Search bots get complete content and metadata. Users get interactivity without sacrificing LCP or INP.

This guide walks through a production landing page: App Router, shadcn/ui forms, framer-motion animations, JSON-LD structured data, and Vercel deployment with PPR enabled.

How PPR Works

At build time, Next.js generates a static HTML skeleton for each page. Components marked as dynamic — wrapped in Suspense, loaded via dynamic(), or using cookies() / headers() — become placeholders filled at request time through React Server Components streaming.

ApproachCrawler HTMLInteractivityTTFB
SSGFullClient-onlyMinimal
SSRFullFullMedium
PPRStatic shell + streamFullMinimal

For SEO landing pages, this means headlines, benefits, FAQ, and JSON-LD stay static. Booking forms, price calculators, and “slots available today” widgets stream in dynamically.

Enable PPR in next.config.ts:

const nextConfig = {
  experimental: {
    ppr: 'incremental',
  },
};
export default nextConfig;

The 'incremental' mode lets you opt in per page with export const experimental_ppr = true — no full-project migration required.

Splitting Static and Dynamic Zones

A typical service landing page has two temperature zones:

Static (prerendered): hero copy, benefits, testimonials, FAQ, footer, Open Graph tags, JSON-LD.

Dynamic (streamed via Suspense): booking forms, price calculators, live availability, UTM-based banners.

Page structure:

export const experimental_ppr = true;

export default function LandingPage() {
  return (
    <>
      <HeroStatic />
      <Suspense fallback={<HeroSkeleton />}>
        <HeroAnimation />
      </Suspense>
      <Benefits />
      <Suspense fallback={<FormSkeleton />}>
        <BookingForm />
      </Suspense>
      <FAQ />
      <Footer />
    </>
  );
}

HeroStatic is a Server Component — indexable text. HeroAnimation is a client-side framer-motion wrapper inside Suspense, so it never blocks the first paint.

Need similar development? Message on Telegram.

SEO: Metadata, JSON-LD, and Core Web Vitals

PPR amplifies good SEO practices rather than replacing them.

Metadata API — title, description, OG tags, and canonical URLs land in static HTML:

export const metadata: Metadata = {
  title: 'Custom Web Development — Next.js, Astro, SEO',
  description: 'High-performance landing pages with Core Web Vitals 90+.',
  alternates: { canonical: 'https://example.com' },
};

JSON-LD — inject schema as a Server Component in your layout. Crawlers read it without executing JavaScript:

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>

Core Web Vitals — INP remains the interaction metric to watch in 2026. PPR helps by keeping LCP elements (hero text, priority images) in the static shell while deferring heavy client bundles (forms, calculators). Use framer-motion’s LazyMotion with domAnimation to shrink the animation bundle tenfold.

Dynamic Components: Forms and Calculators

Booking forms are ideal dynamic holes. Fetch available slots server-side, render the form client-side:

async function BookingFormInner() {
  const slots = await getAvailableSlots();
  return <BookingFormClient slots={slots} />;
}

export function BookingForm() {
  return (
    <Suspense fallback={<FormSkeleton />}>
      <BookingFormInner />
    </Suspense>
  );
}

Submit via Server Actions with revalidatePath after successful booking. Price calculators can run entirely on the client while receiving tariff data as props from a Server Component.

Deploying on Vercel

Vercel is the native host for Next.js 15. PPR works on the Edge Network out of the box:

  1. Connect your repo
  2. Set experimental.ppr: 'incremental' in config
  3. Add export const experimental_ppr = true on target pages
  4. Deploy — Vercel automatically separates the static shell from dynamic holes

Verify with curl — static sections (hero, FAQ) should appear in the initial HTML. Run Lighthouse: target LCP under 2.5s, INP under 200ms, SEO score at 100. Use Google Search Console’s URL Inspection to confirm rendered HTML includes static copy.

Need help? Telegram → or vic.kell@ya.ru

FAQ

Is PPR production-ready in 2026?

PPR is still marked experimental, but incremental opt-in limits blast radius to specific pages. Major teams and Vercel itself use it in production. Watch the Next.js 16 roadmap — stable status may arrive soon.

Can I combine PPR with ISR?

Yes. The static shell can revalidate on an interval while dynamic holes refresh on every request. For landing pages with rarely changing copy, revalidate: 3600 is usually enough.

When should I skip PPR?

Fully static landing pages with no forms or personalization are better served by pure SSG — Astro 5 or Next.js static export. PPR shines when you have at least one dynamic block but need SEO-critical content in the initial HTML.

How does PPR interact with headless CMS?

CMS content (Contentful, Sanity, Strapi) can feed the static shell at build time via generateStaticParams. Reserve dynamic holes for fast-changing data: booking slots, inventory counts, A/B test variants.

Conclusion

Partial Prerendering gives Next.js 15 teams a practical path to SEO landing pages with real interactivity. Static shells deliver fast TTFB, complete crawler HTML, and strong Core Web Vitals. Suspense-wrapped dynamic holes handle forms, calculators, and personalization without full SSR overhead.

The recipe: enable incremental PPR, keep SEO content in Server Components, wrap interactivity in Suspense, add JSON-LD to your layout, deploy on Vercel. For entirely static projects, Astro 5 remains an excellent choice — but if you’re already on Next.js, PPR solves the classic “landing page plus one dynamic feature” problem elegantly.

KEL IT

Need a custom solution?

I build these types of projects professionally. Telegram bots, Mini Apps, websites, mobile and desktop applications. Tell me about your project and I'll get back to you with a plan.