惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Blog of Author Tim Ferriss
S
Schneier on Security
博客园 - 聂微东
爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
有赞技术团队
有赞技术团队
腾讯CDC
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园_首页
J
Java Code Geeks
Last Week in AI
Last Week in AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
Microsoft Azure Blog
Microsoft Azure Blog
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
Y
Y Combinator Blog
Schneier on Security
Schneier on Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
The Register - Security
The Register - Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy & Cybersecurity Law Blog
P
Proofpoint News Feed
P
Privacy International News Feed
K
Kaspersky official blog
C
CERT Recently Published Vulnerability Notes
阮一峰的网络日志
阮一峰的网络日志
F
Full Disclosure
NISL@THU
NISL@THU
AWS News Blog
AWS News Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
MongoDB | Blog
MongoDB | Blog
A
Arctic Wolf
云风的 BLOG
云风的 BLOG
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Threatpost
D
Docker
人人都是产品经理
人人都是产品经理
T
Tailwind CSS Blog
V2EX - 技术
V2EX - 技术
G
GRAHAM CLULEY
M
MIT News - Artificial intelligence
H
Heimdal Security Blog
N
News and Events Feed by Topic
P
Proofpoint News Feed

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
How to Build a $15K Web Design Agency Landing Page With Next.js and Formgrid
Allen Jones · 2026-05-09 · via DEV Community

Most web design agencies charge between $5K and $20K
for a professional landing page. The difference between
a $2K page and a $15K page is rarely the design.
Both might look clean and modern. Both might have good copy.

The difference is what happens after a visitor arrives.

A $2K page has a contact form that sends an email. The
email gets buried. The lead goes cold. The agency never
knows whether that visitor came from Google, a referral, or a paid ad.

A $15K page has a lead capture system. Every enquiry is tracked. Every lead has a status. Every follow-up has a reminder. The agency knows exactly which marketing channels are bringing clients that actually pay.

This tutorial builds the second type. You will build a professional agency landing page in Next.js with a lead capture form powered by Formgrid. Every enquiry becomes a tracked lead with Google Sheets sync built in. No Zapier needed. No separate CRM required.

By the end, you will have a page you can use for your own agency or adapt for clients, and a lead tracking system that works from day one.

What You Are Building

Here is exactly what this tutorial produces:

A professional multi-section agency landing page in
Next.js with a hero section, services overview, social
proof, pricing packages, and a contact and quote request form at the bottom.

Every form submission goes to Formgrid, which turns it into a tracked lead instantly. The lead appears in your Formgrid dashboard with a status of New. It also appears as a new row in a Google Sheet that your whole team can see in real time. You can mark it Contacted when
you reach out, add notes from the conversation, set a follow-up reminder, and mark it Converted when it becomes a paying client.

The stack is Next.js for the frontend, Formgrid for form handling and lead tracking, and Google Sheets as
a shared team dashboard. No backend to build. No
database to manage. No Zapier bill at the end of the month.

Why Most Agency Sites Fail to Convert

Before building anything, it helps to understand why most agency landing pages underperform.

The most common problem is that the contact form is
treated as an afterthought. It sits on a separate Contact page that most visitors never navigate to deliberately.
They land on the homepage, browse the portfolio, and leave without making contact because the path to enquiring is one step too long.

The second problem is that there is no system for what happens after the enquiry arrives. It lands in an email inbox.
The agency responds when they can. There is no record of which enquiries got followed up on, which ones went cold, and no way to know which marketing channel sent them in the first place.

The third problem is that most agency sites look credible but do not build trust fast enough. Visitors need to see specific results, specific client names, and a specific next step within the first few seconds of landing on the page. Vague statements about being
passionate about design, do not convert.

This tutorial fixes all three problems.

Prerequisites

Before starting, make sure you have:

Node.js 18 or higher installed
A Formgrid account at formgrid.dev (free)
A Google account for Sheets integration
Basic familiarity with Next.js and React

Step 1: Set Up Your Next.js Project

Create a new Next.js project:

npx create-next-app@latest agency-landing \
  --typescript --tailwind --app
cd agency-landing
npm run dev

Enter fullscreen mode Exit fullscreen mode

Open localhost:3000 and confirm the default Next.js page loads.

Step 2: Create Your Formgrid Form

Before writing any code, set up your Formgrid form so you have the endpoint URL ready.

Go to formgrid.dev and sign
up using Google or email. No credit card required.


Click New Form and name it something like "Agency
Enquiries" or "Proposal Request Form".
Click Create Form.

Go to the Overview tab and copy your endpoint URL.
It will look like this:
https://api.formgrid.dev/api/f/form_id

Save this URL. You will use it in your form component.

Step 3: Build the Page Structure

Replace the contents of app/page.tsx with this
base structure:

import Hero from '@/components/Hero'
import Services from '@/components/Services'
import SocialProof from '@/components/SocialProof'
import Pricing from '@/components/Pricing'
import ContactForm from '@/components/ContactForm'

export default function Home() {
  return (
    <main>
      <Navbar/>
      <Hero />
      <Services />
      <SocialProof />
      <Pricing />
      <ContactForm />
    </main>
  )
}

Enter fullscreen mode Exit fullscreen mode

Create a components folder inside app and build
each section as a separate component.

Step 4: Build the Hero Section

Create app/components/Hero.tsx:

import Image from 'next/image'

const metrics = [
    { label: 'Revenue generated for clients', value: '$6.8M' },
    { label: 'Average increase in qualified leads', value: '214%' },
    { label: 'Projects shipped with measurable growth', value: '92' },
]

export default function Hero() {
    return (
        <section className="relative overflow-hidden bg-[#06070b] px-6 pt-28 pb-20 text-white md:pt-36">
            <div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(250,204,21,0.2),transparent_35%),radial-gradient(circle_at_80%_20%,rgba(56,189,248,0.15),transparent_30%)]" />
            <div className="relative mx-auto grid w-full max-w-6xl items-center gap-14 lg:grid-cols-[1fr_1fr]">
                <div>
                    <p className="mb-5 text-xs font-semibold uppercase tracking-[0.32em] text-amber-300">
                        Precision web design for serious growth
                    </p>
                    <h1 className="font-display max-w-3xl text-4xl font-black leading-[0.92] tracking-[-0.02em] text-white md:text-5xl lg:text-6xl">
                        Your next website should feel premium and perform like a sales team
                    </h1>
                    <p className="mt-7 max-w-xl text-base leading-7 text-slate-300 md:text-lg">
                        We design fast conversion ready websites for agencies and software
                        brands that need more demand from the same traffic.
                    </p>

                    <div className="mt-10 flex flex-wrap gap-4">
                        <a
                            href="#contact"
                            className="rounded-full bg-amber-300 px-7 py-3 text-sm font-semibold uppercase tracking-[0.2em] text-slate-900 transition hover:bg-amber-200"
                        >
                            Book your strategy call
                        </a>
                        <a
                            href="#services"
                            className="rounded-full border border-slate-600 px-7 py-3 text-sm font-semibold uppercase tracking-[0.2em] text-slate-200 transition hover:border-slate-400 hover:text-white"
                        >
                            Explore capabilities
                        </a>
                    </div>

                    <div className="mt-14 grid gap-4 sm:grid-cols-3">
                        {metrics.map((metric) => (
                            <div
                                key={metric.label}
                                className="rounded-2xl border border-white/10 bg-white/[0.03] p-4 backdrop-blur"
                            >
                                <p className="text-3xl font-semibold text-amber-300">{metric.value}</p>
                                <p className="mt-2 text-xs uppercase tracking-[0.16em] text-slate-400">
                                    {metric.label}
                                </p>
                            </div>
                        ))}
                    </div>
                </div>

                <div className="relative">
                    <div className="absolute -inset-4 rounded-[2rem] bg-gradient-to-tr from-amber-300/30 to-cyan-300/20 blur-2xl" />
                    <div className="relative overflow-hidden rounded-[2rem] border border-white/10 bg-slate-950/70 p-3 shadow-[0_24px_80px_rgba(0,0,0,0.55)]">
                        <Image
                            src="https://images.unsplash.com/photo-1497366811353-6870744d04b2?auto=format&fit=crop&w=1600&q=80"
                            alt="Creative studio workspace with premium website design presentation"
                            width={1200}
                            height={900}
                            className="h-[390px] w-full rounded-[1.5rem] object-cover md:h-[470px] lg:h-[530px]"
                            priority
                        />
                        <div className="mt-4 rounded-xl border border-white/10 bg-black/40 p-4">
                            <p className="text-xs uppercase tracking-[0.2em] text-slate-300">
                                Active project dashboard
                            </p>
                            <p className="mt-2 text-sm text-slate-200">
                                Lead conversion this month is up 39 percent after launch
                            </p>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    )
}

Enter fullscreen mode Exit fullscreen mode


Step 5: Build the Navigation Bar

Create app/components/Navbar.tsx

'use client'

import { useState } from 'react'

const navItems = [
  { label: 'Services', href: '#services' },
  { label: 'Results', href: '#results' },
  { label: 'Pricing', href: '#pricing' },
  { label: 'Contact', href: '#contact' },
]

export default function Navbar() {
  const [isMenuOpen, setIsMenuOpen] = useState(false)

  const handleNavClick = (
    e: React.MouseEvent<HTMLAnchorElement>,
    href: string
  ) => {
    e.preventDefault()
    const target = document.querySelector(href)
    if (!target) return
    target.scrollIntoView({ behavior: 'smooth', block: 'start' })
    setIsMenuOpen(false)
  }

  return (
    <header className="fixed inset-x-0 top-0 z-50 px-6 pt-5">
      <nav className="mx-auto w-full max-w-6xl rounded-2xl border border-white/10 bg-slate-950/70 px-5 py-3 backdrop-blur md:px-7">
        <div className="flex items-center justify-between">
          <a href="#" className="text-sm font-semibold uppercase tracking-[0.2em] text-white">
            AGENCY STUDIO
          </a>

          <ul className="hidden items-center gap-7 md:flex">
            {navItems.map((item) => (
              <li key={item.href}>
                <a
                  href={item.href}
                  onClick={(e) => handleNavClick(e, item.href)}
                  className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-300 transition hover:text-white"
                >
                  {item.label}
                </a>
              </li>
            ))}
          </ul>

          <div className="flex items-center gap-3">
            <a
              href="#contact"
              onClick={(e) => handleNavClick(e, '#contact')}
              className="hidden rounded-full bg-amber-300 px-4 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-900 transition hover:bg-amber-200 md:inline-block md:px-5"
            >
              Book call
            </a>
            <button
              type="button"
              aria-label="Toggle navigation menu"
              aria-expanded={isMenuOpen}
              onClick={() => setIsMenuOpen((prev) => !prev)}
              className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-white/15 text-slate-200 transition hover:border-white/30 hover:text-white md:hidden"
            >
              <span className="text-lg">{isMenuOpen ? 'x' : '='}</span>
            </button>
          </div>
        </div>

        {isMenuOpen && (
          <div className="mt-4 border-t border-white/10 pt-4 md:hidden">
            <ul className="space-y-3">
              {navItems.map((item) => (
                <li key={`mobile-${item.href}`}>
                  <a
                    href={item.href}
                    onClick={(e) => handleNavClick(e, item.href)}
                    className="block rounded-lg px-2 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-200 transition hover:bg-white/5 hover:text-white"
                  >
                    {item.label}
                  </a>
                </li>
              ))}
            </ul>
            <a
              href="#contact"
              onClick={(e) => handleNavClick(e, '#contact')}
              className="mt-4 inline-block rounded-full bg-amber-300 px-5 py-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-900 transition hover:bg-amber-200"
            >
              Book call
            </a>
          </div>
        )}
      </nav>
    </header>
  )
}

Enter fullscreen mode Exit fullscreen mode




Step 6: Build the Services Section

Create app/components/Services.tsx:

import Image from 'next/image'

const services = [
  {
    title: 'Demand focused websites',
    description:
      'A complete website system built to guide visitors from curiosity to qualified enquiry.',
    deliverables: [
      'Messaging architecture',
      'Visual identity direction',
      'Conversion focused pages',
    ],
    image:
      'https://images.unsplash.com/photo-1487014679447-9f8336841d58?auto=format&fit=crop&w=1600&q=80',
  },
  {
    title: 'Premium launch experiences',
    description:
      'Launch pages and campaign experiences engineered to make your offer feel impossible to ignore.',
    deliverables: [
      'Launch strategy workshop',
      'Sales page copy structure',
      'Rapid split test setup',
    ],
    image:
      'https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&w=1600&q=80',
  },
  {
    title: 'Client acquisition systems',
    description:
      'Connected forms analytics and follow up logic so every qualified lead reaches your team instantly.',
    deliverables: [
      'Advanced enquiry flows',
      'CRM and sheet automation',
      'Performance reporting',
    ],
    image:
      'https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&w=1600&q=80',
  },
]

export default function Services() {
  return (
    <section id="services" className="bg-[#090b10] px-6 py-24 text-white md:py-32">
      <div className="mx-auto w-full max-w-6xl">
        <div className="mb-14 flex flex-wrap items-end justify-between gap-8">
          <div>
            <p className="text-xs font-semibold uppercase tracking-[0.32em] text-cyan-300">
              What your investment includes
            </p>
            <h2 className="mt-4 max-w-3xl text-4xl font-semibold leading-tight text-white md:text-5xl">
              Every engagement combines strategic thinking with polished execution
            </h2>
          </div>
          <p className="max-w-md text-sm leading-7 text-slate-300">
            You work directly with senior designers and developers from kickoff to
            launch so your project keeps speed and quality.
          </p>
        </div>

        <div className="grid gap-6 lg:grid-cols-3">
          {services.map((service) => (
            <div
              key={service.title}
              className="overflow-hidden rounded-3xl border border-white/10 bg-white/[0.03] shadow-[0_20px_50px_rgba(0,0,0,0.35)] transition hover:-translate-y-1 hover:border-cyan-300/30"
            >
              <Image
                src={service.image}
                alt={service.title}
                width={1000}
                height={700}
                className="h-52 w-full object-cover"
              />
              <div className="p-7">
                <h3 className="text-2xl font-semibold text-white">{service.title}</h3>
                <p className="mt-3 text-sm leading-7 text-slate-300">
                  {service.description}
                </p>
                <ul className="mt-6 space-y-2">
                  {service.deliverables.map((item) => (
                    <li key={item} className="text-sm text-slate-200">
                      {item}
                    </li>
                  ))}
                </ul>
                <a
                  href="#contact"
                  className="mt-7 inline-block rounded-full border border-slate-500 px-5 py-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-200 transition hover:border-cyan-300 hover:text-cyan-200"
                >
                  Start this scope
                </a>
              </div>
            </div>
          ))}
        </div>

        <div className="mt-10 rounded-3xl border border-white/10 bg-slate-950/70 p-8 md:p-10">
          <p className="text-xs font-semibold uppercase tracking-[0.2em] text-cyan-300">
            Delivery confidence
          </p>
          <p className="mt-3 max-w-4xl text-lg leading-8 text-slate-200">
            Every project follows a clear sequence with weekly checkpoints design
            review sessions and launch QA so your team sees progress at every step.
          </p>
        </div>
      </div>
    </section>
  )
}

Enter fullscreen mode Exit fullscreen mode


Step 6: Build the Social Proof Section

Create app/components/SocialProof.tsx:

import Image from 'next/image'

const testimonials = [
  {
    quote:
      'This launch gave our team instant credibility. Clients now say our site looks like the top firm in our market.',
    name: 'Elena Ross',
    title: 'Managing Partner at Meridian Legal',
    image:
      'https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?auto=format&fit=crop&w=500&q=80',
  },
  {
    quote:
      'Our enquiry quality improved within the first week. We closed more deals without increasing ad spend.',
    name: 'James Holloway',
    title: 'Founder at North Ridge Growth',
    image:
      'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=500&q=80',
  },
  {
    quote:
      'The experience feels premium from first click to submission. Every detail reflects confidence and trust.',
    name: 'Priya Raman',
    title: 'Marketing Director at Atlas Health',
    image:
      'https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=500&q=80',
  },
]

export default function SocialProof() {
  return (
    <section id="results" className="bg-[#06070b] px-6 py-24 text-white md:py-32">
      <div className="mx-auto w-full max-w-6xl">
        <div className="mb-14 flex flex-wrap items-end justify-between gap-8">
          <div>
            <p className="text-xs font-semibold uppercase tracking-[0.32em] text-amber-300">
              Trusted by ambitious teams
            </p>
            <h2 className="mt-4 text-4xl font-semibold leading-tight text-white md:text-5xl">
              Client stories from recent launches
            </h2>
          </div>
          <div className="rounded-2xl border border-white/10 bg-white/[0.03] px-6 py-4">
            <p className="text-xs uppercase tracking-[0.2em] text-slate-400">
              Average launch rating
            </p>
            <p className="mt-2 text-3xl font-semibold text-amber-300">4.98</p>
          </div>
        </div>

        <div className="grid gap-6 md:grid-cols-3">
          {testimonials.map((t) => (
            <article
              key={t.name}
              className="rounded-3xl border border-white/10 bg-white/[0.03] p-7"
            >
              <div className="mb-5 flex items-center gap-4">
                <Image
                  src={t.image}
                  alt={t.name}
                  width={70}
                  height={70}
                  className="h-14 w-14 rounded-full border border-white/15"
                />
                <div>
                  <p className="text-sm font-semibold text-white">{t.name}</p>
                  <p className="text-xs uppercase tracking-[0.14em] text-slate-400">
                    {t.title}
                  </p>
                </div>
              </div>
              <p className="text-sm leading-7 text-slate-200">
                &quot;{t.quote}&quot;
              </p>
            </article>
          ))}
        </div>
      </div>
    </section>
  )
}

Enter fullscreen mode Exit fullscreen mode


Step 7: Build the Pricing Section

Create app/components/Pricing.tsx:

const packages = [
  {
    name: 'Essential',
    price: '$6000',
    description: 'For teams that need a refined digital presence quickly',
    features: [
      'Complete homepage and service pages',
      'Offer and messaging workshop',
      'Lead enquiry form and automation setup',
      'Performance setup and launch QA',
    ],
  },
  {
    name: 'Signature',
    price: '$11000',
    description: 'Our most selected package for serious growth campaigns',
    features: [
      'High fidelity brand aligned design system',
      'Custom copy structure for each major page',
      'Conversion journey and analytics map',
      'Team enablement and launch training session',
      'Thirty day optimization support',
    ],
    featured: true,
  },
  {
    name: 'Flagship',
    price: '$15000',
    description: 'Complete world class website program for market leaders',
    features: [
      'Full strategy discovery and growth planning',
      'Premium UI system and advanced interactions',
      'Integrated lead qualification logic',
      'Content framework and authority sections',
      'Ninety day advisory support after launch',
    ],
  },
]

export default function Pricing() {
  return (
    <section id="pricing" className="bg-[#090b10] px-6 py-24 text-white md:py-32">
      <div className="mx-auto w-full max-w-6xl">
        <p className="text-xs font-semibold uppercase tracking-[0.32em] text-amber-300">
          Investment tiers
        </p>
        <h2 className="mt-4 text-4xl font-semibold leading-tight text-white md:text-5xl">
          Pick the level that matches your growth targets
        </h2>

        <div className="mt-12 grid gap-6 lg:grid-cols-3">
          {packages.map((pkg) => (
            <article
              key={pkg.name}
              className={`rounded-3xl border p-8 ${
                pkg.featured
                  ? 'border-amber-300 bg-amber-300 text-slate-900 shadow-[0_20px_60px_rgba(250,204,21,0.22)]'
                  : 'border-white/10 bg-white/[0.03]'
              }`}
            >
              <p
                className={`text-xs font-semibold uppercase tracking-[0.2em] ${
                  pkg.featured ? 'text-slate-800' : 'text-amber-300'
                }`}
              >
                {pkg.name}
              </p>
              <p className="mt-4 text-5xl font-semibold">{pkg.price}</p>
              <p
                className={`mt-3 text-sm leading-7 ${
                  pkg.featured ? 'text-slate-800' : 'text-slate-300'
                }`}
              >
                {pkg.description}
              </p>

              <ul className="mt-7 space-y-3">
                {pkg.features.map((feature) => (
                  <li key={feature} className="flex items-start gap-3 text-sm">
                    <span className={pkg.featured ? 'text-slate-900' : 'text-amber-300'}></span>
                    <span>{feature}</span>
                  </li>
                ))}
              </ul>
            </article>
          ))}
        </div>
      </div>
    </section>
  )
}

Enter fullscreen mode Exit fullscreen mode

Step 8: Build the Contact Form With Formgrid

This is the most important section. Create
app/components/ContactForm.tsx:

'use client'

import Image from 'next/image'
import { useState } from 'react'

export default function ContactForm() {
    const [status, setStatus] = useState<'idle' | 'sending' | 'success' | 'error'>(
        'idle'
    )

    const FORMGRID_ENDPOINT =
        'https://api.formgrid.dev/forms/your-form-id/submissions'

    async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault()

        const form = e.currentTarget
        const honey = (form.elements.namedItem('_honey') as HTMLInputElement).value

        if (honey) return

        setStatus('sending')

        const formData = new FormData(form)

        const name = `${formData.get('firstName') ?? ''} ${formData.get('lastName') ?? ''}`.trim()
        const service = (formData.get('service') as string) ?? 'Unknown service'
        const budget = (formData.get('budget') as string) ?? 'No budget selected'

        formData.set('_subject', `New Enquiry: ${name} | ${service} | ${budget}`)

        try {
            const res = await fetch(FORMGRID_ENDPOINT, {
                method: 'POST',
                body: formData,
            })

            if (res.ok) {
                setStatus('success')
            } else {
                setStatus('error')
            }
        } catch {
            setStatus('error')
        }
    }

    return (
        <section id="contact" className="bg-[#06070b] px-6 py-24 text-white md:py-32">
            <div className="mx-auto grid w-full max-w-6xl gap-8 lg:grid-cols-[0.9fr_1.1fr]">
                <aside className="rounded-3xl border border-white/10 bg-white/[0.03] p-6 md:p-8">
                    <p className="text-xs font-semibold uppercase tracking-[0.3em] text-amber-300">
                        Let us build your next growth engine
                    </p>
                    <h2 className="mt-4 text-3xl font-semibold leading-tight md:text-4xl">
                        Request your custom proposal
                    </h2>
                    <p className="mt-4 text-sm leading-7 text-slate-300">
                        Share your goals and our team will send scope pricing and timeline
                        recommendations within one business day.
                    </p>
                    <Image
                        src="https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&w=1600&q=80"
                        alt="Agency team reviewing website strategy"
                        width={1000}
                        height={1000}
                        className="mt-6 rounded-2xl border border-white/10"
                    />
                </aside>

                {status === 'success' ? (
                    <div className="rounded-3xl border border-amber-300 bg-amber-300/10 p-10 text-center">
                        <p className="text-xs font-semibold uppercase tracking-[0.25em] text-amber-200">
                            Enquiry received
                        </p>
                        <p className="mt-4 text-3xl font-semibold text-white">Thank you</p>
                        <p className="mt-3 text-sm text-slate-200">
                            We will reply with your proposal details within one business day.
                        </p>
                    </div>
                ) : (
                    <form
                        onSubmit={handleSubmit}
                        className="rounded-3xl border border-white/10 bg-slate-950/60 p-6 md:p-8"
                    >
                        <input
                            type="text"
                            name="_honey"
                            style={{ display: 'none' }}
                            tabIndex={-1}
                            autoComplete="off"
                        />

                        <div className="grid gap-4 sm:grid-cols-2">
                            <label className="text-sm text-slate-300">
                                First name
                                <input
                                    type="text"
                                    id="firstName"
                                    name="firstName"
                                    required
                                    placeholder="Jordan"
                                    className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                                />
                            </label>

                            <label className="text-sm text-slate-300">
                                Last name
                                <input
                                    type="text"
                                    id="lastName"
                                    name="lastName"
                                    required
                                    placeholder="Reed"
                                    className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                                />
                            </label>
                        </div>

                        <label className="mt-4 block text-sm text-slate-300">
                            Email address
                            <input
                                type="email"
                                id="email"
                                name="email"
                                required
                                placeholder="jordan@company.com"
                                className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                            />
                        </label>

                        <div className="mt-4 grid gap-4 sm:grid-cols-2">
                            <label className="text-sm text-slate-300">
                                Service needed
                                <select
                                    id="service"
                                    name="service"
                                    required
                                    className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                                >
                                    <option value="">Select service</option>
                                    <option>Demand focused website</option>
                                    <option>Premium launch experience</option>
                                    <option>Client acquisition system</option>
                                </select>
                            </label>

                            <label className="text-sm text-slate-300">
                                Budget range
                                <select
                                    id="budget"
                                    name="budget"
                                    className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                                >
                                    <option value="">Select range</option>
                                    <option>$6000 to $9000</option>
                                    <option>$10000 to $14000</option>
                                    <option>$15000 and above</option>
                                </select>
                            </label>
                        </div>

                        <label className="mt-4 block text-sm text-slate-300">
                            Project goals
                            <textarea
                                id="message"
                                name="message"
                                rows={6}
                                required
                                placeholder="Tell us what you are launching who you want to reach and what success looks like."
                                className="mt-2 w-full rounded-xl border border-slate-700 bg-slate-900 px-4 py-3 text-white outline-none transition focus:border-amber-300"
                            />
                        </label>

                        <button
                            type="submit"
                            disabled={status === 'sending'}
                            className="mt-6 rounded-full bg-amber-300 px-7 py-3 text-sm font-semibold uppercase tracking-[0.2em] text-slate-900 transition hover:bg-amber-200 disabled:cursor-not-allowed disabled:opacity-60"
                        >
                            {status === 'sending' ? 'Sending' : 'Send proposal request'}
                        </button>

                        {status === 'error' && (
                            <p className="mt-4 text-sm text-red-300">
                                Something went wrong. Please try again or contact us by email.
                            </p>
                        )}
                    </form>
                )}
            </div>
        </section>
    )
}

Enter fullscreen mode Exit fullscreen mode

Replace your-form-id in the FORMGRID_ENDPOINT
constant with your actual Formgrid form ID from
Step 2.

Step 9: Connect Google Sheets

In your Formgrid dashboard, go to your form settings
and click the Integrations tab. Connect your Google
account and select the sheet where you want enquiries
to land.




From this point, every form submission creates a new row in that sheet automatically. The columns match your form fields exactly. Your team can see new enquiries in real time without logging into anything.

Step 10: Test the Full Flow

Start your development server:

npm run dev

Enter fullscreen mode Exit fullscreen mode

Navigate to the contact form section and submit a test enquiry with real-looking data.



Within seconds, you should see three things happen.

The submission also appears in your
Leads tab as a tracked lead with
a status, a timestamp, and all the Submitter details.

The submission appears in your Formgrid dashboard with a status of New.

An email notification arrives at your Formgrid signup email with the full submission details.

A new row appears in your connected Google Sheet with every field populated automatically.

If all three appear, your lead capture system is working correctly.

The Lead Pipeline in Action

Go to your Formgrid dashboard and click on the test submission you just created.

You will see the lead with a status of New. This is the starting state for every enquiry that arrives.

Click the status and change it to Contacted.
This records that you have reached out to this person.
Your conversion rate updates automatically.

Click Add Note and type something like "Spoke on
the phone. Interested in the Growth package.
Needs two weeks to confirm budget." That note lives on the lead permanently.

Click Set Follow Up and choose a date five days from now.
On that date, Formgrid will email you with the lead details and your note attached. You will not need to remember to follow up. The
reminder does it for you.



This is the difference between an agency that closes 40% of its enquiries and one that closes 15%. Not skill. Not price. Just follow up on consistency.

What Makes This a $15K Page

The page you just built has every element that separates a premium agency site from a basic one.

A clear value proposition in the hero that speaks directly to the outcome the client wants. Not "we are passionate about design" but "we build websites that convert."

Specific social proof with real numbers. Percentage lift in conversions. Revenue generated. Projects delivered. Vague testimonials do not build trust. Specific results do.

Service packages with transparent pricing. Clients
who see pricing upfront are more qualified when they
enquire. They have already decided the price is acceptable before filling out the form.

A strong single call to action. Every section points to the same place. Get a Quote. Nothing competes with it.

Lead tracking is built in from day one. This is the element most agency sites completely miss. Every enquiry is captured, tracked, and followed up on systematically. No lead goes cold because someone forgot to reply.

The last point is what justifies the $15K price tag to clients. You are not just delivering a website. You are delivering a system that actively generates and tracks revenue for their business.

Using This for Your Own Agency

If you are building this for your own freelance or agency business, replace the placeholder copy with your real services, real results, and real pricing.

Add your actual testimonials. Replace the statistics
with real numbers from your own projects. Update the service descriptions to match what you actually build.

The form endpoint is already connected to Formgrid.
The Google Sheets sync is already configured. The lead pipeline is already working.

Every client enquiry that comes in through this page will appear in your dashboard as a tracked lead, in your Google Sheet as a new row, and in your inbox as an email notification within seconds of submission.

Deploying to Production

When you are ready to go live, deploy to Vercel in one command:

npx vercel

Enter fullscreen mode Exit fullscreen mode

Vercel detects Next.js automatically and deploys with zero configuration. Your form will work identically in production because Formgrid handles all the server-side processing.

Final Thoughts

The gap between a $2K website and a $15K website
is not the design framework, the number of animations, or the size of the portfolio section.

It is whether the site has a system for turning visitors into trackable, followable, convertible leads.

A beautiful static site with a contact form that sends emails is a $2K site. The same design with Formgrid powering the form, Google Sheets sync running in the background, and a lead pipeline
tracking every enquiry from New to Converted is a $15K site.

The only technical difference is one endpoint URL and a ten-minute Formgrid setup.

If you want to build this for your own agency or for a client, start free at
formgrid.dev. The form
backend, lead pipeline, and Google Sheets sync
are all included from day one.

For a done-for-you version, where we build the entire system for you, see our
agency setup packages.

👉 Start free at formgrid.dev