Las landing pages siguen una fórmula probada. El visitante llega con una pregunta, "¿esto resuelve mi problema?", y cada sección lo acerca al "sí" o lo pierde por el camino. En esta guía recorremos la fórmula de 8 secciones, cómo componer esas secciones en Next.js App Router y los patrones técnicos que mantienen el resultado rápido y fácil de mantener.
La fórmula de la landing page
Los estudios sobre optimización de conversión coinciden en un mismo orden de secciones. Este es el orden y la razón de ser de cada una:
| # | Sección | Función | |---|---------|---------| | 1 | Hero | Expone la propuesta de valor. Capta la atención. | | 2 | Carrusel de logos | Aporta prueba social justo después del gancho. | | 3 | Características | Muestra cómo el producto cumple la promesa. | | 4 | Prueba social | Testimonios o casos de éxito, la voz de personas reales. | | 5 | Demo del producto | Demo visual, carrusel de capturas o recorrido guiado. | | 6 | Precios | Elimina la objeción del "¿cuánto cuesta?". | | 7 | FAQ | Resuelve las últimas dudas antes de la compra. | | 8 | CTA | Una invitación final, sin fricción, para actuar. |
No es una fórmula rígida. Un producto B2B enterprise puede saltarse los precios públicos y sustituirlos por un "Contacta con ventas". Una app de consumo quizá empiece con la prueba social. Aun así, este orden ofrece la mayor tasa de conversión base para un SaaS self-serve.
Componer secciones en Next.js App Router
En Next.js 15 con App Router, tu landing page vive en app/[locale]/(public)/page.tsx. Cada sección es un archivo independiente dentro de src/registry/sections/:
// app/[locale]/(public)/page.tsx
import HeroCentered from "@/registry/sections/hero/hero-centered/HeroCentered";
import LogoCarousel from "@/registry/sections/logo-carousel/logo-carousel-marquee/LogoCarouselMarquee";
import FeaturesGrid from "@/registry/sections/features/features-grid/FeaturesGrid";
import Testimonials from "@/registry/sections/testimonials/testimonials-cards/TestimonialsCards";
import PricingToggle from "@/registry/sections/pricing/pricing-toggle/PricingToggle";
import FAQ from "@/registry/sections/faq/faq-accordion/FaqAccordion";
import CTABanner from "@/registry/sections/cta/cta-centered/CtaCentered";
import { heroMock } from "@/registry/sections/hero/hero-centered/mock";
import { pricingMock } from "@/registry/sections/pricing/pricing-toggle/mock";
export default function HomePage() {
return (
<main>
<HeroCentered {...heroMock} />
<LogoCarousel />
<FeaturesGrid id="features" />
<Testimonials id="social-proof" />
<PricingToggle id="pricing" {...pricingMock} />
<FAQ id="faq" />
<CTABanner />
</main>
);
}
Los Server Components se renderizan al instante en tiempo de build (o bajo petición). La directiva "use client" solo aparece en las secciones que necesitan interactividad (toggle, animaciones). Eso significa que la mayor parte de tu landing page es HTML estático, ideal para los Core Web Vitals.
Separadores de sección
Cuando dos secciones comparten color de fondo, se confunden visualmente. Un componente separador crea aire y separación sin sumar <div>s vacíos:
// components/SectionDivider.tsx
interface SectionDividerProps {
variant?: "line" | "wave" | "angle" | "none";
flip?: boolean;
}
export function SectionDivider({ variant = "line", flip = false }: SectionDividerProps) {
if (variant === "none") return null;
if (variant === "line") {
return (
<div
style={{
height: "1px",
background: "var(--color-border)",
margin: "0 var(--container-padding-x)",
}}
/>
);
}
if (variant === "wave") {
return (
<div
style={{
overflow: "hidden",
transform: flip ? "scaleY(-1)" : "none",
lineHeight: 0,
}}
>
<svg
viewBox="0 0 1440 56"
style={{ display: "block", width: "100%" }}
aria-hidden
>
<path
d="M0,28 C360,56 1080,0 1440,28 L1440,56 L0,56 Z"
fill="var(--color-background-alt)"
/>
</svg>
</div>
);
}
return null;
}
Úsalo entre secciones con fondos distintos:
<HeroCentered />
<SectionDivider variant="wave" />
<FeaturesGrid />
Navegación por anclas
En landing pages de una sola página, los enlaces ancla permiten saltar a cada sección. Añade atributos id a los contenedores de sección y enlázalos con #pricing, #features, etc.:
// Pass an id to each section component
interface SectionProps {
id?: string;
}
// Inside the section component
<section
id={id}
style={{
scrollMarginTop: "4rem", // Account for the fixed navbar height
paddingTop: "var(--section-padding-y)",
paddingBottom: "var(--section-padding-y)",
background: "var(--color-background)",
}}
>
La propiedad CSS scroll-margin-top es clave: sin ella, el navbar fijo tapa el encabezado de la sección cuando se activa el enlace ancla.
En el navbar:
const navLinks = [
{ label: "Features", href: "#features" },
{ label: "Pricing", href: "#pricing" },
{ label: "FAQ", href: "#faq" },
];
Para un desplazamiento suave, añade esto a tu CSS global:
html {
scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
Lazy loading de las secciones bajo el pliegue
El hero y la primera sección visible deben cargarse de inmediato. Todo lo que queda bajo el pliegue puede cargarse en diferido con dynamic de Next.js:
import dynamic from "next/dynamic";
// Load immediately (above the fold)
import HeroCentered from "@/registry/sections/hero/hero-centered/HeroCentered";
import LogoCarousel from "@/registry/sections/logo-carousel/logo-carousel-marquee/LogoCarouselMarquee";
// Lazy load (below the fold)
const FeaturesGrid = dynamic(
() => import("@/registry/sections/features/features-grid/FeaturesGrid"),
{ loading: () => <SectionSkeleton height={500} /> }
);
const Testimonials = dynamic(
() => import("@/registry/sections/testimonials/testimonials-cards/TestimonialsCards"),
{ loading: () => <SectionSkeleton height={400} /> }
);
const PricingToggle = dynamic(
() => import("@/registry/sections/pricing/pricing-toggle/PricingToggle"),
{ loading: () => <SectionSkeleton height={600} /> }
);
El componente SectionSkeleton reserva el espacio vertical para evitar saltos de layout mientras la sección carga:
function SectionSkeleton({ height }: { height: number }) {
return (
<div
style={{
height,
background: "var(--color-background)",
animation: "pulse 1.5s ease-in-out infinite",
}}
/>
);
}
Este enfoque reduce el bundle inicial de JavaScript y mueve el JS no crítico a peticiones de red posteriores, lo que mejora el Time to Interactive (TTI).
Patrón de animación de sección
Cada sección debería animarse al entrar en el viewport. El patrón habitual usa whileInView de Framer Motion junto con viewport={{ once: true }}:
import { motion } from "framer-motion";
const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];
// Section header
<motion.div
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease }}
viewport={{ once: true, margin: "-80px" }}
>
<h2>Section heading</h2>
</motion.div>
El margin: "-80px" dispara la animación 80px antes de que el elemento entre en el viewport, así ya está animándose cuando el usuario lo ve, y no después.
Ensamblarlo todo
La estructura completa de una landing page en Next.js:
app/[locale]/(public)/
├── page.tsx ← section composition
└── layout.tsx ← shared layout (navbar, footer)
src/registry/sections/
├── hero/hero-centered/
├── logo-carousel/logo-carousel-marquee/
├── features/features-grid/
├── testimonials/testimonials-cards/
├── pricing/pricing-toggle/
├── faq/faq-accordion/
└── cta/cta-centered/
Cada carpeta de sección contiene tres archivos: el componente (PascalCase.tsx), sus metadatos (meta.ts) y sus datos de ejemplo (mock.ts). Así cada sección es portable por sí sola: copia los tres archivos a cualquier otro proyecto y funciona.
Explora todas las secciones
El catálogo de Incubator reúne 449 variantes de sección repartidas en 66 tipos: heroes, características, testimonios, precios, FAQs, CTAs, footers y más. Cada variante se construye con el mismo sistema de tokens CSS y está lista para copy-paste en proyectos Next.js.
Relacionado en incubator
- Secciones hero en React: variantes de hero copy-paste para landing pages.
- Secciones de precios en React: componentes de precios con toggle, tabla y tarjetas.
- Secciones CTA en React: banners de llamada a la acción listos para el pie de cualquier página.
- Componentes de animación al hacer scroll: secciones con efectos de entrada activados por el viewport.
- Incubator frente a Tailwind UI: cómo se comparan las bibliotecas de secciones copy-paste.