How to Build a SaaS Landing Page with Next.js
A SaaS landing page has one job: get a visitor to take an action. Sign up, start a trial, buy. Everything else is in service of that.
Most tutorials cover the technical setup. This covers the whole thing, including the parts that actually affect whether people convert.
What goes on a SaaS landing page
Before writing a line of code, get clear on the sections. The order matters.
Hero. Above the fold. One clear headline, one subheadline, one primary CTA. The headline should say what the product does, not what it aspires to be. "AI inference SDK for TypeScript" converts better than "Unlock the power of AI for your team."
Social proof. As close to the hero as possible. Logos, a quote, a number. This is the fastest way to borrow trust from brands your visitor already knows.
Features or how it works. Not a list of capabilities. A short explanation of how the product solves the problem. Three to five items. Concrete, not abstract.
Pricing. On the page. Hiding pricing is a conversion killer for most SaaS products. One to three tiers, clear differentiators, a clear recommended option.
FAQ. Address the objections that stop people from clicking buy. Think about the tab someone has open next to yours.
Final CTA. Repeat the primary action at the bottom. Some people scroll all the way before deciding.
Setting up the project
Use Next.js 15 with the App Router. Server components by default, client only where needed.
npx create-next-app@latest my-saas --typescript --tailwind --app
cd my-saas
npm install
Structure the landing page as composable sections:
src/
├── app/
│ └── page.tsx # Assembles all sections
├── components/
│ └── sections/
│ ├── Hero.tsx
│ ├── Features.tsx
│ ├── Pricing.tsx
│ ├── FAQ.tsx
│ └── CTA.tsx
└── lib/
└── data/
└── defaults.ts # All copy in one place
Keep all user-facing strings in defaults.ts. Components receive copy as props and render it. This makes copy changes a one-file diff and lets you swap content without touching component logic.
Building the Hero section
// src/components/sections/Hero.tsx
import Link from 'next/link'
interface HeroContent {
eyebrow: string
heading: string
subheading: string
ctaLabel: string
ctaHref: string
}
export function Hero({ content }: { content: HeroContent }) {
return (
<section className="hero">
<p className="hero-eyebrow">{content.eyebrow}</p>
<h1 className="hero-heading">{content.heading}</h1>
<p className="hero-sub">{content.subheading}</p>
<Link href={content.ctaHref} className="btn-primary">
{content.ctaLabel}
</Link>
</section>
)
}
Use Link from next/link for all internal navigation. Plain <a> tags skip the router and break prefetching.
Building the Pricing section
Pricing is where most SaaS landing pages lose conversions. Keep it simple.
interface PricingTier {
name: string
price: string
period: string
features: string[]
ctaLabel: string
ctaHref: string
featured?: boolean
}
export function Pricing({ tiers }: { tiers: PricingTier[] }) {
return (
<section className="pricing">
<div className="pricing-grid">
{tiers.map((tier) => (
<div key={tier.name} className={`pricing-card${tier.featured ? ' featured' : ''}`}>
<div className="pricing-name">{tier.name}</div>
<div className="pricing-price">
{tier.price}
<span className="pricing-period">{tier.period}</span>
</div>
<ul className="pricing-features">
{tier.features.map((f) => (
<li key={f}>{f}</li>
))}
</ul>
<Link href={tier.ctaHref} className="btn-pricing">
{tier.ctaLabel}
</Link>
</div>
))}
</div>
</section>
)
}
One tier should be visually distinct. Use a border, background, or badge to indicate the recommended option. If everything looks equal, people spend time comparing instead of deciding.
Assembling the page
// src/app/page.tsx
import { Hero } from '@/components/sections/Hero'
import { Features } from '@/components/sections/Features'
import { Pricing } from '@/components/sections/Pricing'
import { FAQ } from '@/components/sections/FAQ'
import { CTA } from '@/components/sections/CTA'
import { defaults } from '@/lib/data/defaults'
export default function HomePage() {
return (
<main>
<Hero content={defaults.hero} />
<Features content={defaults.features} />
<Pricing tiers={defaults.pricing} />
<FAQ items={defaults.faq} />
<CTA content={defaults.cta} />
</main>
)
}
Every section is a server component. No 'use client' unless the section needs interactivity, like a form or accordion.
What most tutorials skip
Copy is the hardest part. The component structure takes an afternoon. Writing copy that converts takes longer. Spend more time on the headline than on the code. Read it out loud. If it sounds like a press release, rewrite it.
Mobile layout needs a plan. Don't design desktop and add responsive as an afterthought. The hero section especially needs a clear mobile stacking order. Hero heading, then subheading, then CTA, then any supporting visual.
Page speed matters for conversion. Next.js handles a lot here by default, but watch your image sizes. Use next/image for anything visual in the hero. A slow hero is a conversion killer.
One CTA per section. Give visitors one thing to do at a time. Secondary links and nav items compete with your primary action. Keep the nav minimal. Consider no nav at all above the fold.
Skip the blank page
If you want a starting point that handles the structure and design so you can focus on copy and product, ShipUI has Next.js landing page themes built on this architecture. TypeScript strict, Tailwind v4, all copy in defaults.ts. Same pattern as above, already styled. What to look for in a Next.js SaaS starter covers how to evaluate your options if you're comparing a few.
Start from scratch if you want to. Start from a template if you want to ship faster.