Background
I constantly need to build landing pages fast — for side projects, small clients, or testing an MVP. After trying a bunch of different approaches, I settled on a stack that lets me go from idea to live URL in a single workday.
This post walks through exactly that process.
My Stack
| Layer | Tool | Why |
|---|---|---|
| Framework | Next.js 16 App Router | Server Components, zero-config deploy |
| UI Components | shadcn/ui | Accessible, customizable, own code |
| Styling | Tailwind CSS v4 | Utility-first, fast |
| Hosting | Vercel | Fastest deploy for Next.js |
| Analytics | Plausible | Lightweight, no cookies |
| Design ref | Figma (Free plan) | Quick wireframing |
Step 1: Project Setup (15 minutes)
💻bash# Create Next.js project npx create-next-app@latest my-landing --typescript --tailwind --app # Enter the directory cd my-landing # Init shadcn/ui npx shadcn@latest init # Add commonly used components npx shadcn@latest add button card badge separator
When initializing shadcn, pick:
- Style: Default
- Base color: Slate (or Zinc for dark mode)
- CSS variables: Yes
Step 2: Layout Structure (30 minutes)
A solid landing page is made up of these sections:
💻tsx// app/page.tsx export default function Home() { return ( <main> <Navbar /> {/* Logo + nav links + CTA button */} <Hero /> {/* Headline + subtext + primary CTA */} <SocialProof /> {/* Logos, testimonials, số liệu */} <Features /> {/* 3-6 tính năng chính */} <Pricing /> {/* Bảng giá */} <FAQ /> {/* Câu hỏi thường gặp */} <CTA /> {/* Final call to action */} <Footer /> {/* Links + copyright */} </main> ); }
Each section is its own Server Component — easy to maintain, easy to reorder.
Step 3: Hero Section with shadcn/ui
💻tsximport { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; export function Hero() { return ( <section className="py-20 px-4 text-center max-w-4xl mx-auto"> <Badge variant="secondary" className="mb-4"> 🚀 Mới ra mắt </Badge> <h1 className="text-4xl sm:text-6xl font-bold tracking-tight mb-6"> Làm sản phẩm <span className="text-primary"> nhanh hơn</span> </h1> <p className="text-xl text-muted-foreground mb-8 max-w-2xl mx-auto"> Mô tả ngắn gọn giá trị sản phẩm mang lại. Tập trung vào outcome, không phải features. </p> <div className="flex gap-3 justify-center flex-wrap"> <Button size="lg" asChild> <a href="#pricing">Bắt đầu miễn phí</a> </Button> <Button size="lg" variant="outline" asChild> <a href="#features">Xem demo →</a> </Button> </div> </section> ); }
💡 A great headline = [Target user] uses [Product] to [achieve outcome] without [pain point].
Step 4: Typography with Inter
Inter is the go-to font for landing pages — readable, professional, and free:
💻tsx// app/layout.tsx import { Inter } from "next/font/google"; const inter = Inter({ subsets: ["latin"], display: "swap", variable: "--font-inter", }); export default function RootLayout({ children }) { return ( <html lang="vi" className={inter.variable}> <body className="font-sans antialiased">{children}</body> </html> ); }
Step 5: Deploy to Vercel (5 minutes)
💻bash# Push to GitHub git init && git add . && git commit -m "Initial landing page" git remote add origin git@github.com:username/repo.git git push -u origin main
Then:
- Go to vercel.com → New Project
- Import your GitHub repo
- Click Deploy
Vercel auto-detects Next.js, configures the build, and deploys in 30 seconds.
ℹ️ The free Hobby plan supports custom domains, automatic SSL, and unlimited Git deploys.
Step 6: Add Analytics (10 minutes)
Once it's live, you'll want to know who's visiting:
💻tsx// app/layout.tsx — add Plausible import Script from "next/script"; // Inside <body>: <Script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js" />
Plausible is 45× lighter than Google Analytics, and no cookie banner required.
Pre-launch Checklist
- Mobile responsive (test in Chrome DevTools)
- Page title and meta description
- OG image (1200×630px) for social sharing
- Favicon
- CTA button works (correct links, email form functional)
- Analytics active
- Load time < 3s (check PageSpeed Insights)
- Custom domain set up (if applicable)
Real-world Timeline
With this stack, a typical day looks like:
- 9:00 AM: Start from create-next-app
- 12:00 PM: UI done, responsive, dark mode working
- 2:00 PM: Deployed live with custom domain
- 3:00 PM: Analytics active, ready to share
Total: 6 hours from idea to a production-ready live URL.
Resources
- shadcn/ui — Component library (free)
- Vercel — Hosting (free tier)
- Plausible Analytics — Analytics ($9/month)
- Figma — Design wireframe (free tier)
Related Articles
Optimizing Next.js App Router Performance: From 60 to 98 on PageSpeed
A battle-tested checklist for improving Core Web Vitals in Next.js 15+: image optimization, font loading, bundle analysis, caching strategy, and monitoring with Plausible. Includes real before/after numbers.
12 min read →
DevOpsHow to Deploy Next.js App Router to a Ubuntu VPS with Docker and Nginx
A step-by-step guide: multi-stage Dockerfile, Docker Compose, Nginx reverse proxy, free SSL with Certbot, and automated CI/CD with GitHub Actions.
12 min read →
Found this useful?
Subscribe to get the latest technical articles and reviews from CHAEI PUEI Tech.
Subscribe for free