Landing pages have one job: convert visitors. Every millisecond of load time, every line of unused JavaScript, every layout shift costs you conversions. Astro ships zero JavaScript by default and generates static HTML that loads instantly on any connection. This guide builds a conversion-focused landing page with a hero section, feature grid, social proof, pricing, and a call-to-action — all scoring 100 on every Lighthouse metric.
Prerequisites
- →Node.js 20+
Astro requires Node.js 20 or later for its build tooling and dev server.
- →Basic HTML and CSS Knowledge
Astro components use standard HTML with scoped styles. No framework experience needed.
- →Copy and Assets Ready
Have your headline, subheadline, feature descriptions, and any images prepared. Writing copy during development slows you down.
Create the Astro Project with Tailwind CSS
Scaffold a new Astro project and add Tailwind CSS for utility-first styling. Astro's minimal template gives you a clean starting point without unnecessary boilerplate. Tailwind pairs perfectly with Astro because both prioritize shipping only what you use.
npm create astro@latest -- --template minimal my-landing-page
cd my-landing-page
npx astro add tailwind
npm installTip: The minimal template is intentionally bare — it includes only the essentials so you're not deleting demo content.
Tip: Run 'npm run dev' to start the dev server at localhost:4321 with hot module replacement.
Build the Hero Section with a Clear Value Proposition
Create a hero component with a bold headline, supporting subheadline, and a primary CTA button. The hero is the most important section — it must communicate what your product does and who it's for in under 5 seconds. Use a contrasting CTA color and plenty of whitespace to draw the eye.
---
// src/components/Hero.astro
interface Props {
headline: string;
subheadline: string;
ctaText: string;
ctaHref: string;
}
const { headline, subheadline, ctaText, ctaHref } = Astro.props;
---
<section class="relative overflow-hidden px-6 py-24 md:px-16 md:py-32">
<div class="mx-auto max-w-4xl text-center">
<h1 class="text-5xl font-bold leading-tight tracking-tight text-white md:text-7xl">
{headline}
</h1>
<p class="mx-auto mt-6 max-w-2xl text-lg text-gray-400 md:text-xl">
{subheadline}
</p>
<div class="mt-10 flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
<a
href={ctaHref}
class="rounded-lg bg-emerald-500 px-8 py-4 text-lg font-semibold text-black transition-colors hover:bg-emerald-400"
>
{ctaText}
</a>
<a
href="#features"
class="rounded-lg border border-gray-700 px-8 py-4 text-lg font-semibold text-gray-300 transition-colors hover:border-gray-500"
>
Learn More
</a>
</div>
<p class="mt-6 text-sm text-gray-500">
No credit card required · Free 14-day trial
</p>
</div>
<div class="pointer-events-none absolute inset-0 -z-10">
<div class="absolute left-1/2 top-0 h-[500px] w-[800px] -translate-x-1/2 rounded-full bg-emerald-500/10 blur-3xl" />
</div>
</section>Tip: Always add a trust signal below the CTA ('No credit card required', 'Used by 10k+ teams') to reduce friction.
Tip: Use a subtle gradient or blur behind the hero text to create visual depth without adding image weight.
Create the Features Grid with Icons
Build a features section that showcases 3-6 key benefits in a responsive grid. Each feature has an icon, title, and one-sentence description. Focus on benefits (what the user gets) not features (what the product does). Keep descriptions under 20 words each.
---
// src/components/Features.astro
const features = [
{
icon: "⚡",
title: "Lightning Fast",
description:
"Sub-second page loads with zero client-side JavaScript. Your users never wait.",
},
{
icon: "🔒",
title: "Secure by Default",
description:
"End-to-end encryption, SOC 2 compliance, and automatic security updates.",
},
{
icon: "📊",
title: "Real-Time Analytics",
description:
"Track conversions, user behavior, and revenue with built-in dashboards.",
},
{
icon: "🔗",
title: "Seamless Integrations",
description:
"Connect with Slack, Stripe, GitHub, and 200+ tools in one click.",
},
{
icon: "🌍",
title: "Global CDN",
description:
"Deployed to 50+ edge locations worldwide for consistent performance everywhere.",
},
{
icon: "🛠️",
title: "Developer First",
description:
"Full API access, CLI tools, and SDKs in every major language.",
},
];
---
<section id="features" class="px-6 py-24 md:px-16">
<div class="mx-auto max-w-6xl">
<div class="text-center">
<p class="text-sm font-semibold uppercase tracking-wider text-emerald-400">
Features
</p>
<h2 class="mt-2 text-4xl font-bold text-white">
Everything you need to ship faster
</h2>
</div>
<div class="mt-16 grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{features.map((feature) => (
<div class="rounded-xl border border-gray-800 bg-gray-900/50 p-6 transition-all hover:border-emerald-500/30">
<div class="text-3xl">{feature.icon}</div>
<h3 class="mt-4 text-lg font-semibold text-white">{feature.title}</h3>
<p class="mt-2 text-sm leading-relaxed text-gray-400">
{feature.description}
</p>
</div>
))}
</div>
</div>
</section>Tip: Use emoji icons for prototyping, then replace with SVG icons (Lucide or Heroicons) for production polish.
Tip: Three columns on desktop, two on tablet, one on mobile — this is the most readable grid pattern for feature cards.
Add Social Proof with Testimonials
Build a testimonials section with customer quotes, names, roles, and company logos. Social proof is the single most persuasive element on a landing page after the hero. Show 2-3 testimonials from recognizable companies or roles that match your target audience.
---
// src/components/Testimonials.astro
const testimonials = [
{
quote:
"We cut our deployment time from 45 minutes to under 2 minutes. The developer experience is unmatched.",
name: "Sarah Chen",
role: "CTO",
company: "Streamline AI",
avatar: "/avatars/sarah.jpg",
},
{
quote:
"The analytics dashboard alone saved us from buying three separate tools. Everything we need is in one place.",
name: "Marcus Johnson",
role: "Head of Engineering",
company: "NovaPay",
avatar: "/avatars/marcus.jpg",
},
{
quote:
"Our page load times dropped by 70% after migrating. Customers noticed the difference immediately.",
name: "Priya Patel",
role: "Product Lead",
company: "HealthSync",
avatar: "/avatars/priya.jpg",
},
];
---
<section class="px-6 py-24 md:px-16">
<div class="mx-auto max-w-6xl">
<div class="text-center">
<p class="text-sm font-semibold uppercase tracking-wider text-emerald-400">
Testimonials
</p>
<h2 class="mt-2 text-4xl font-bold text-white">
Trusted by engineering teams
</h2>
</div>
<div class="mt-16 grid gap-8 md:grid-cols-3">
{testimonials.map((t) => (
<div class="rounded-xl border border-gray-800 bg-gray-900/50 p-6">
<p class="text-gray-300 leading-relaxed">
“{t.quote}”
</p>
<div class="mt-6 flex items-center gap-3">
<img
src={t.avatar}
alt={t.name}
width={40}
height={40}
class="rounded-full"
loading="lazy"
/>
<div>
<p class="text-sm font-semibold text-white">{t.name}</p>
<p class="text-xs text-gray-500">
{t.role}, {t.company}
</p>
</div>
</div>
</div>
))}
</div>
</div>
</section>Tip: Use specific numbers in testimonials ('45 minutes to 2 minutes', '70% faster') — specificity is more credible than vague praise.
Tip: Add loading='lazy' to avatar images since testimonials are usually below the fold.
Build the Pricing Section
Create a pricing section with 2-3 tiers that clearly communicate what each plan includes. Highlight the most popular plan with a distinct border or badge. Use a simple grid layout where each tier shows the price, feature list, and a CTA button. Pricing transparency builds trust and qualifies leads before they sign up.
---
// src/components/Pricing.astro
const plans = [
{
name: "Starter",
price: 0,
period: "forever",
description: "Perfect for side projects and experiments.",
features: [
"Up to 3 projects",
"1,000 monthly builds",
"Community support",
"Basic analytics",
],
cta: "Get Started Free",
highlighted: false,
},
{
name: "Pro",
price: 29,
period: "/month",
description: "For growing teams that need more power.",
features: [
"Unlimited projects",
"50,000 monthly builds",
"Priority support",
"Advanced analytics",
"Custom domains",
"Team collaboration",
],
cta: "Start Free Trial",
highlighted: true,
},
{
name: "Enterprise",
price: null,
period: "",
description: "For organizations with advanced security needs.",
features: [
"Everything in Pro",
"Unlimited builds",
"SSO & SAML",
"Dedicated support",
"SLA guarantee",
"Custom integrations",
],
cta: "Contact Sales",
highlighted: false,
},
];
---
<section id="pricing" class="px-6 py-24 md:px-16">
<div class="mx-auto max-w-6xl">
<div class="text-center">
<p class="text-sm font-semibold uppercase tracking-wider text-emerald-400">
Pricing
</p>
<h2 class="mt-2 text-4xl font-bold text-white">
Simple, transparent pricing
</h2>
<p class="mt-4 text-gray-400">
Start free. Upgrade when you're ready.
</p>
</div>
<div class="mt-16 grid gap-8 md:grid-cols-3">
{plans.map((plan) => (
<div
class={`relative rounded-xl border p-8 ${
plan.highlighted
? "border-emerald-500 bg-emerald-500/5"
: "border-gray-800 bg-gray-900/50"
}`}
>
{plan.highlighted && (
<span class="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-emerald-500 px-3 py-1 text-xs font-bold text-black">
Most Popular
</span>
)}
<h3 class="text-xl font-semibold text-white">{plan.name}</h3>
<p class="mt-2 text-sm text-gray-400">{plan.description}</p>
<div class="mt-6">
{plan.price !== null ? (
<span class="text-5xl font-bold text-white">
${plan.price}
</span>
) : (
<span class="text-3xl font-bold text-white">Custom</span>
)}
{plan.period && (
<span class="text-gray-500">{plan.period}</span>
)}
</div>
<ul class="mt-8 space-y-3">
{plan.features.map((feature) => (
<li class="flex items-center gap-2 text-sm text-gray-300">
<svg
class="h-4 w-4 text-emerald-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5 13l4 4L19 7"
/>
</svg>
{feature}
</li>
))}
</ul>
<a
href="#"
class={`mt-8 block rounded-lg py-3 text-center font-semibold transition-colors ${
plan.highlighted
? "bg-emerald-500 text-black hover:bg-emerald-400"
: "border border-gray-700 text-gray-300 hover:border-emerald-500"
}`}
>
{plan.cta}
</a>
</div>
))}
</div>
</div>
</section>Tip: Always highlight one plan as 'Most Popular' — it anchors the decision and most visitors will pick it.
Tip: Show the free plan first to lower the perceived risk. Users who start free convert to paid at higher rates than those who never sign up.
Add the Final CTA and Assemble the Page
Create a strong closing CTA section and wire all components together in the index page. The closing CTA repeats your value proposition and gives visitors who scrolled through the entire page one final chance to convert. Use the same primary CTA button as the hero for visual consistency.
---
// src/pages/index.astro
import Layout from "../layouts/Layout.astro";
import Hero from "../components/Hero.astro";
import Features from "../components/Features.astro";
import Testimonials from "../components/Testimonials.astro";
import Pricing from "../components/Pricing.astro";
---
<Layout title="Ship Faster — The Developer Platform for Modern Teams">
<Hero
headline="Ship faster without breaking things"
subheadline="The all-in-one platform for building, deploying, and monitoring web applications. Zero config, instant deploys, real-time observability."
ctaText="Start Building Free"
ctaHref="#pricing"
/>
<Features />
<Testimonials />
<Pricing />
<!-- Final CTA -->
<section class="px-6 py-24 md:px-16">
<div class="mx-auto max-w-3xl rounded-2xl border border-emerald-500/20 bg-emerald-500/5 px-8 py-16 text-center">
<h2 class="text-4xl font-bold text-white">
Ready to ship your next project?
</h2>
<p class="mt-4 text-lg text-gray-400">
Join 10,000+ developers who build and deploy with confidence.
</p>
<a
href="#pricing"
class="mt-8 inline-block rounded-lg bg-emerald-500 px-8 py-4 text-lg font-semibold text-black transition-colors hover:bg-emerald-400"
>
Start Building Free
</a>
</div>
</section>
<!-- Footer -->
<footer class="border-t border-gray-800 px-6 py-12 md:px-16">
<div class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-4 md:flex-row">
<p class="text-sm text-gray-500">
© {new Date().getFullYear()} YourCompany. All rights reserved.
</p>
<div class="flex gap-6">
<a href="/privacy" class="text-sm text-gray-500 hover:text-white">Privacy</a>
<a href="/terms" class="text-sm text-gray-500 hover:text-white">Terms</a>
<a href="mailto:hello@yourcompany.com" class="text-sm text-gray-500 hover:text-white">Contact</a>
</div>
</div>
</footer>
</Layout>Tip: Mirror the hero CTA text in the closing CTA — repetition reinforces the action you want visitors to take.
Tip: Add a subtle border and background tint to the final CTA section so it stands out from the rest of the page.
Optimize and Deploy to Vercel
Run a production build to verify everything compiles correctly, check your Lighthouse scores, and deploy. Astro generates static HTML that any CDN can serve. Vercel auto-detects Astro and configures the build for you. Your landing page should score 100 on Performance, Accessibility, Best Practices, and SEO.
# Build and preview locally
npm run build
npm run preview
# Check output size — should be tiny
ls -la dist/
# Push to GitHub
git init
git add .
git commit -m "Landing page with hero, features, testimonials, and pricing"
gh repo create my-landing-page --public --push
# Deploy to Vercel
npm install -g vercel
vercel --prodTip: Run Lighthouse in incognito mode to get scores without browser extensions interfering.
Tip: Astro's static output is typically under 50KB total for a landing page — if yours is larger, check for unoptimized images.
Next Steps
- →Add a newsletter signup form with Resend or ConvertKit integration for lead capture.
- →Implement A/B testing on your hero headline using Vercel's edge middleware to optimize conversion rates.
- →Add a changelog or updates section to show the product is actively developed.
- →Set up analytics with Plausible or Fathom for privacy-friendly visitor tracking.