Build a landing page in Next.js with shadcn-compatible blocks

Build a Next.js landing page from free shadcn-compatible blocks: section order, copy that converts, performance, and metadata and structured data for SEO.

By MiniDev13 min read

To build a landing page in Next.js quickly, keep app/page.tsx a server component that exports metadata, and compose it from eight client blocks in a fixed order: navigation, hero, product, how it works, social proof, pricing, FAQ and a final call to action. MiniDev UI ships each of those as a free shadcn-compatible block with real props, so the work left is your copy, your product visuals and your numbers. This guide covers the order, the props that matter, copy, performance and the SEO basics.

The section order and why

Visitors arrive with one question ("is this for me?") and leave with another ("is it worth it?"). The order answers them in sequence:

SectionBlockIts one job
Navigationfloating-navOrient, and keep the primary CTA one click away
Herohero-spotlightSay what it is, who it is for and what to do next
Productbento-liveShow the product doing the three to six things people buy it for
How it worksscroll-storyRemove the fear of setup with three concrete steps
Social proofsocial-proof-wallShow that people like the visitor already use it
Pricingpricing-plansMake the cost and the right plan obvious
FAQfaq-sectionAnswer the objections that stop the click
Final CTActa-bandAsk once more, with the lowest friction form possible

Social proof can move up directly under the hero when you have recognizable logos. Pricing can move down or out entirely for sales-led products; keep the FAQ either way.

Install the blocks and tokens

bash
npx shadcn@latest add https://ui.minidev.pro/r/floating-nav.json https://ui.minidev.pro/r/hero-spotlight.json https://ui.minidev.pro/r/bento-live.json https://ui.minidev.pro/r/scroll-story.json
npx shadcn@latest add https://ui.minidev.pro/r/social-proof-wall.json https://ui.minidev.pro/r/pricing-plans.json https://ui.minidev.pro/r/faq-section.json https://ui.minidev.pro/r/cta-band.json https://ui.minidev.pro/r/light-provider.json

The blocks use MiniDev's semantic tokens (bg-surface, text-fg-muted, shadow-raised) and a few utilities such as light-spot and text-lit. Add the token stylesheet once, either from the package (npm i minidev-ui-kit) or by copying styles.css into your project:

app/globals.csscss
@import "tailwindcss";
@import "minidev-ui-kit/styles.css";
@source "../node_modules/minidev-ui-kit";

Render LightProvider once in your root layout. It writes the light position to CSS variables from the pointer, which is what makes the hero headline and the CTA panel catch the light. Its idle prop, on by default, lets the light drift slowly on touch devices and when the pointer is still; pass idle={false} to keep it pinned to the pointer.

Compose the page

Every block is a client component, but your page does not need to be. Keep app/page.tsx a server component so it can export metadata, and pass only serializable props: strings, arrays, hrefs and React elements. Function props such as onSubmit go through a small client wrapper.

app/page.tsxtsx
import Image from "next/image"
import { FloatingNav } from "@/components/ui/floating-nav"
import { HeroSpotlight } from "@/components/ui/hero-spotlight"
import { BentoLive } from "@/components/ui/bento-live"
import { ScrollStory } from "@/components/ui/scroll-story"
import { SocialProofWall } from "@/components/ui/social-proof-wall"
import { PricingPlans } from "@/components/ui/pricing-plans"
import { FaqSection } from "@/components/ui/faq-section"
import { Signup } from "./signup"
import { FEATURES, STEPS, QUOTES, PLANS, FAQS } from "./content"

export default function Home() {
  return (
    <>
      <FloatingNav
        brand={<span className="font-semibold">Acme</span>}
        links={[
          { label: "Product", href: "#product" },
          { label: "How it works", href: "#how" },
          { label: "Pricing", href: "#pricing" },
          { label: "FAQ", href: "#faq" },
        ]}
        cta={{ label: "Start free", href: "/signup" }}
        secondary={{ label: "Sign in", href: "/login" }}
      />
      <main>
        <HeroSpotlight
          eyebrow="New: Slack alerts"
          title="Revenue answers,"
          highlight="before Monday."
          description="Acme watches Stripe and your CRM and tells finance what changed and why."
          primary={{ label: "Start free", href: "/signup" }}
          secondary={{ label: "See a 2 minute demo", href: "#how" }}
          proof="Free for 14 days. No card required."
          visual={<Image src="/app.png" alt="Acme dashboard showing MRR and alerts" width={2400} height={1500} preload sizes="(min-width: 1280px) 1152px, 100vw" className="rounded-2xl border border-border shadow-overlay" />}
        />
        <div id="product" className="scroll-mt-24"><BentoLive title="Everything finance checks, checked for you." items={FEATURES} /></div>
        <div id="how" className="scroll-mt-24"><ScrollStory title="Live in an afternoon." steps={STEPS} /></div>
        <div id="customers" className="scroll-mt-24"><SocialProofWall logos={["Northwind", "Halcyon"]} quotes={QUOTES} /></div>
        <div id="pricing" className="scroll-mt-24"><PricingPlans plans={PLANS} /></div>
        <div id="faq" className="scroll-mt-24"><FaqSection items={FAQS} /></div>
        <Signup />
      </main>
    </>
  )
}

scroll-mt-24 on each anchor target keeps section headings clear of the sticky nav when a link jumps to them. The same structure, with the default content, is the Lumen template, which is a good reference for spacing and rhythm between the blocks.

Notes on each block

FloatingNav

FloatingNav starts as a full-width bar and, after 40px of scroll, tucks into a floating pill with a raised surface. The hover highlight glides between links, and on mobile a menu sheet drops from the pill. Props: brand, links ({ label, href }[]), cta ({ label, href?, onClick? }) and secondary (pass null to hide it). Keep it to four or five links.

HeroSpotlight

HeroSpotlight renders the page h1 from title plus a second, lit line from highlight. It also takes eyebrow, description, primary and secondary actions, a small proof line, an optional visual that rises in last, and align ("center" or "left"). Put your strongest real product visual in visual. The entrance animation is skipped when the visitor prefers reduced motion.

BentoLive

BentoLive is a six-column grid on large screens. Each item has title, body, an optional visual and a span of 2, 3, 4 or 6 columns, plus tall to span two rows. Make each row add up to six. The idea is that tiles show a small working piece of the product (a live number, a streaming answer, a list of alerts) instead of static screenshots.

ScrollStory

ScrollStory lists steps ({ kicker, title, body, visual }) on the left and pins the active step's visual on the right, switching as each step crosses the middle of the viewport. A rail fills with progress. On small screens each step shows its visual inline, so nothing is lost on mobile. Three steps is the sweet spot.

SocialProofWall

SocialProofWall combines a logo marquee that pauses on hover with a three-column wall of quotes. Quotes are { quote, name, role, metric? }; the optional metric renders as a pill above the quote and is the most persuasive part, so use it. The sample content is fictional. Replace it with real customers before you ship.

PricingPlans and FaqSection

Both are covered in depth in the React pricing page guide: plan data, the yearly toggle with rolling prices, the featured plan, and an accessible accordion FAQ where one answer is open at a time.

CtaBand

CtaBand is a lit panel with a headline and an email form. onSubmit(email) may return a promise: the button shows a busy state while it runs, morphs into the success message when it resolves, and returns to idle if it throws, so errors are handled by throwing.

app/signup.tsxtsx
"use client"
import { CtaBand } from "@/components/ui/cta-band"

export function Signup() {
  return (
    <CtaBand
      button="Get early access"
      success="Check your inbox"
      note="No card. Unsubscribe in one click."
      onSubmit={async (email) => {
        const res = await fetch("/api/waitlist", { method: "POST", body: JSON.stringify({ email }) })
        if (!res.ok) throw new Error("Signup failed")
      }}
    />
  )
}
Hero SpotlightblockA launch hero with a pulsing eyebrow, blur-in headline with a cursor lit second line, two actions, a proof line and a product visual that tilts into place.npx shadcn@latest add ui.minidev.pro/r/hero-spotlight.jsonBento LiveblockA React bento feature grid where each tile runs a live demo: a rolling MRR counter, a typed AI answer, ranked alerts and an orbiting integrations ring.npx shadcn@latest add ui.minidev.pro/r/bento-live.json

Copy that does the work

  • Hero title: the outcome, not the category. "Revenue answers before Monday" beats "AI-powered analytics platform".
  • Hero description: who it is for and how it works, in one sentence under 25 words.
  • One primary action, repeated with the same label in the nav, hero, pricing and final CTA.
  • Feature tiles: a verb and a result in the title, a specific detail in the body. Numbers beat adjectives.
  • Steps: start each with a verb ("Connect", "Learn", "Tell") and state the time each takes.
  • Quotes: pick ones that name a before and after. Put the measurable part in metric.
  • FAQ: answer the questions sales hears, especially security, data, contracts and switching cost.

Performance

Client components still render to HTML on the server, so all copy is in the first response and crawlable. What you pay for on the client is JavaScript for the interactive parts and the Motion library that drives the animations. Keep that cost in check:

  • Your hero visual is usually the Largest Contentful Paint element. Serve it with next/image, give it sizes, and mark it preload (priority before Next.js 16).
  • The hero headline animates in with a short fade. If LCP is your headline and you measure a delay, shorten the delay or drop the initial animation on the h1 in your copy of the block.
  • Use next/font for fonts so they are self-hosted and do not shift layout. MiniDev UI uses Geist via the geist package.
  • Keep below-the-fold visuals light. Live tiles built from HTML and CSS are cheaper than autoplaying video.
  • Every animated block respects prefers-reduced-motion, and marquees stop entirely with motion-reduce:animate-none.

Test on a mid-range phone with network throttling, not only on your laptop. Landing page traffic from ads and social is mostly mobile.

Metadata and SEO basics

Set metadataBase in the root layout, then give the home page a specific title, a description of 140 to 160 characters, a canonical URL and Open Graph data. Add an opengraph-image.tsx next to the page to generate the social image with ImageResponse from next/og.

app/page.tsxtsx
import type { Metadata } from "next"

export const metadata: Metadata = {
  title: "Acme: revenue alerts for finance teams",
  description: "Acme watches Stripe, your CRM and your warehouse, and tells finance what changed, why, and what to do next. Free for 14 days.",
  alternates: { canonical: "/" },
  openGraph: { type: "website", url: "/", siteName: "Acme", title: "Acme: revenue alerts for finance teams" },
  twitter: { card: "summary_large_image" },
}

Add structured data for the product as JSON-LD in the page. Escape < so the JSON can never close the script tag:

tsx
const jsonLd = {
  "@context": "https://schema.org",
  "@type": "SoftwareApplication",
  name: "Acme",
  applicationCategory: "BusinessApplication",
  operatingSystem: "Web",
  offers: { "@type": "Offer", price: "0", priceCurrency: "USD" },
}

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }}
/>
  • One h1 per page. HeroSpotlight renders it; every other block renders its title as an h2, which gives the page a clean outline.
  • Add app/sitemap.ts and app/robots.ts so crawlers find the page and its siblings.
  • Give the hero image real alt text that describes the product screen.
  • Link to deeper pages (docs, pricing, use cases) with descriptive anchor text, not "learn more".

Templates to start from

The templates page has complete landing pages built from these blocks. Lumen uses exactly the stack in this guide on the glass material. Open one next to your page and compare section spacing and headline length. If you want the page, the product behind it and the launch handled end to end, the MiniDev studio builds complete products with this kit.

Components used

All free and MIT licensed. For a matching dark theme without a flash on load, see dark mode in Next.js.

Floating NavblockA sticky marketing navbar that tucks into a floating pill after you scroll, with a hover highlight that glides between links and an animated mobile menu.npx shadcn@latest add ui.minidev.pro/r/floating-nav.jsonScroll StoryblockA scrollytelling section: steps scroll on one side while a pinned visual swaps as each reaches mid screen and a progress rail fills. Inline on mobile.npx shadcn@latest add ui.minidev.pro/r/scroll-story.jsonSocial Proof WallblockA testimonial wall with a logo marquee that pauses on hover and three columns of quote cards drifting at different speeds, with metric chips and edge fades.npx shadcn@latest add ui.minidev.pro/r/social-proof-wall.jsonCta BandblockA closing CTA section with a large lit headline and an inline email form whose button animates from idle to sending to a success check. Async onSubmit.npx shadcn@latest add ui.minidev.pro/r/cta-band.json
bash
npx shadcn@latest add https://ui.minidev.pro/r/<name>.json

Components used in this guide

Floating NavblockA sticky marketing navbar that tucks into a floating pill after you scroll, with a hover highlight that glides between links and an animated mobile menu.npx shadcn@latest add ui.minidev.pro/r/floating-nav.jsonHero SpotlightblockA launch hero with a pulsing eyebrow, blur-in headline with a cursor lit second line, two actions, a proof line and a product visual that tilts into place.npx shadcn@latest add ui.minidev.pro/r/hero-spotlight.jsonBento LiveblockA React bento feature grid where each tile runs a live demo: a rolling MRR counter, a typed AI answer, ranked alerts and an orbiting integrations ring.npx shadcn@latest add ui.minidev.pro/r/bento-live.jsonScroll StoryblockA scrollytelling section: steps scroll on one side while a pinned visual swaps as each reaches mid screen and a progress rail fills. Inline on mobile.npx shadcn@latest add ui.minidev.pro/r/scroll-story.jsonSocial Proof WallblockA testimonial wall with a logo marquee that pauses on hover and three columns of quote cards drifting at different speeds, with metric chips and edge fades.npx shadcn@latest add ui.minidev.pro/r/social-proof-wall.jsonPricing PlansblockA React pricing section with a monthly or yearly switch that rolls prices to the new value, a featured ink plan, and feature rows marked included or not.npx shadcn@latest add ui.minidev.pro/r/pricing-plans.jsonFaq SectionblockAn FAQ section with a heading column and an accessible accordion: one answer open at a time, arrow key navigation, aria-expanded and smooth row animation.npx shadcn@latest add ui.minidev.pro/r/faq-section.jsonCta BandblockA closing CTA section with a large lit headline and an inline email form whose button animates from idle to sending to a success check. Async onSubmit.npx shadcn@latest add ui.minidev.pro/r/cta-band.json

Frequently asked questions

What sections should a SaaS landing page have?

Navigation, a hero with one clear action, a product section, how it works, social proof, pricing, an FAQ and a final call to action. Move social proof up if you have well-known customer logos.

Can a Next.js page export metadata if it uses client components?

Yes, as long as page.tsx itself is not marked "use client". A server component page can render client blocks and still export metadata or generateMetadata.

Are shadcn landing page blocks good for SEO?

They can be. Client components still render to HTML on the server, so the copy is crawlable. What matters is one h1, a sensible heading outline, real alt text, fast LCP and proper metadata.

Is there a free Next.js landing page template?

Yes. MiniDev UI's templates, such as Lumen, are built entirely from free MIT blocks you can install with the shadcn CLI and edit in your own repo.

More guides