Landing Pages
2 variantes de landing page con componentes listos para usar
Landing Pages
EmpiezaTuSaaS incluye 2 variantes de landing page optimizadas para conversión. Todos los componentes están en components/landing/ y se exportan desde components/landing/index.ts.
Variantes disponibles
1. Landing Page Principal (/)
La landing completa con todas las secciones. Úsala cuando tu producto ya está listo para vender.
Secciones en orden: Header → Hero → FeaturesSection → Pricing → FAQ → CTA → Footer
import {
Header,
Hero,
FeaturesSection,
Pricing,
FAQ,
CTA,
Footer,
} from "@/components/landing";
export default async function HomePage() {
return (
<div className="light bg-background font-kaio relative min-h-screen">
<Header />
<main>
<Hero />
<FeaturesSection />
<Pricing />
<FAQ />
<CTA />
</main>
<Footer />
</div>
);
}2. Waitlist — Lista de espera (/waitlist)
Página enfocada para validar tu idea antes de lanzar. Sin distracciones: solo logo, formulario de email y lista de beneficios.
Cuándo usarla: cuando todavía no tienes el producto listo pero quieres capturar emails de interesados.
import { WaitlistHero } from "@/components/landing/waitlist-hero";
import { Header } from "@/components/landing/header";
import { Footer } from "@/components/landing/footer";
import { setRequestLocale, getTranslations } from "next-intl/server";
type Props = { params: Promise<{ locale: string }> };
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "waitlist" });
return {
title: t("pageTitle"),
description: t("pageDescription", { name: "Tu SaaS" }),
};
}
export default async function WaitlistPage({ params }: Props) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="light min-h-screen flex flex-col">
<Header minimal />
<main className="flex-1">
<WaitlistHero />
</main>
<Footer />
</div>
);
}La waitlist usa <Header minimal /> — solo muestra el logo y el selector de idioma, sin menú de navegación ni botones de CTA. Esto mantiene al usuario enfocado en el formulario.
Cómo funciona la captura de emails:
- El usuario escribe su email y hace click en el botón
WaitlistHerohace unPOSTa/api/leadcon{ email, source: "waitlist" }- La API envía un email de notificación a
siteConfig.supportEmailcon los datos del lead - El usuario ve un mensaje de confirmación
Header: modo normal vs minimal
El componente Header acepta un prop minimal que controla qué se muestra:
| Prop | Qué muestra | Cuándo usarlo |
|---|---|---|
<Header /> | Logo + navLinks + Login + CTA + menú móvil | Landing principal (/) |
<Header minimal /> | Solo logo + selector de idioma | Waitlist, páginas de captura |
// Landing principal - Header completo con navegación
<Header />
// Waitlist - Header minimal, sin distracciones
<Header minimal />API de captura de leads (/api/lead)
La waitlist usa el endpoint /api/lead para capturar emails. Cuando alguien se apunta, recibes un email de notificación:
import { NextRequest, NextResponse } from "next/server";
import { resend } from "@/lib/email";
import { siteConfig } from "@/config/site";
export async function POST(request: NextRequest) {
const { email, name, source } = await request.json();
if (!email) {
return NextResponse.json(
{ message: "El email es requerido" },
{ status: 400 }
);
}
// Enviar notificación al admin
await resend.emails.send({
from: `${siteConfig.name} <${siteConfig.noReplyEmail}>`,
to: siteConfig.supportEmail,
subject: `Nuevo lead: ${email}`,
html: `
<h2>Nuevo lead capturado</h2>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Nombre:</strong> ${name || "No proporcionado"}</p>
<p><strong>Fuente:</strong> ${source || "Landing page"}</p>
<p><strong>Fecha:</strong> ${new Date().toLocaleString("es-ES")}</p>
`,
});
return NextResponse.json({ success: true });
}Para que la captura de emails funcione, necesitas tener configuradas las variables RESEND_API_KEY y los emails en config/site.ts (supportEmail y noReplyEmail).
Componentes de Landing
Todos los componentes se exportan desde components/landing/index.ts:
| Componente | Archivo | Descripción |
|---|---|---|
Header | header.tsx | Navegación sticky con menú móvil. Acepta prop minimal |
Hero | hero.tsx | Hero principal con video demo, social proof y CTA |
FeaturesSection | features-section.tsx | Accordion interactivo con categorías (Auth, Pagos, Email, Stack) |
Pricing | pricing.tsx | Planes de precio con toggle mensual/anual y CheckoutButton |
FAQ | faq.tsx | Preguntas frecuentes con accordion colapsable |
CTA | cta.tsx | Call-to-action final con botón de compra |
Footer | footer.tsx | Footer con links legales y de producto |
WaitlistHero | waitlist-hero.tsx | Hero con formulario de email para lista de espera |
LanguageSwitcher | language-switcher.tsx | Botón para cambiar entre ES/EN |
Personalización
Cambiar textos
Los textos de los componentes están en los ficheros de traducción (messages/es/landing.json y messages/en/landing.json). Para la waitlist, edita messages/es/waitlist.json.
Cambiar links de navegación
Edita config/site.ts:
navLinks: [
{ label: "Características", href: "#features" },
{ label: "Precios", href: "#pricing" },
{ label: "FAQ", href: "#faq" },
],Cambiar planes de precio
Edita config/stripe.ts. Cambia nombres, precios y features para adaptarlos a tu producto.
Cambiar FAQ
Los textos del FAQ están en messages/es/landing.json bajo la clave faq.items.
Usar la waitlist como landing principal
Si tu producto aún no está listo, puedes hacer que / muestre la waitlist en vez de la landing completa:
import { Header, WaitlistHero, Footer } from "@/components/landing";
export default async function HomePage() {
return (
<div className="light min-h-screen flex flex-col">
<Header minimal />
<main className="flex-1">
<WaitlistHero />
</main>
<Footer />
</div>
);
}Cuando tu producto esté listo, cambia <WaitlistHero /> por <Hero /> y <Header minimal /> por <Header />, y añade el resto de secciones.
Eliminar la waitlist
Si no necesitas la página de waitlist:
- Elimina la carpeta:
rm -rf app/[locale]/waitlist/ - Opcional — elimina los ficheros de mensajes:
messages/es/waitlist.json,messages/en/waitlist.json - Opcional — elimina el import en
i18n/request.ts:
// Elimina esta línea si ya no usas waitlist
...(await import(`../messages/${locale}/waitlist.json`)).default,Estructura de rutas
| Ruta | Fichero | Uso |
|---|---|---|
/ | app/[locale]/page.tsx | Landing completa (Hero + Features + Pricing + FAQ + CTA) |
/waitlist | app/[locale]/waitlist/page.tsx | Lista de espera con formulario de email |
Ambas rutas están disponibles en español (/, /waitlist) e inglés (/en, /en/waitlist).
Modo claro
Las 2 landing pages fuerzan modo claro con className="light" en su div raíz. El dashboard y las páginas de autenticación respetan el tema configurado por el usuario.
SEO
La landing principal incluye JSON-LD de tipo Organization y SoftwareApplication para mejorar el SEO. Estos componentes están en components/seo/json-ld.tsx.