El problema con los "Widgets"
La mayoría de las plataformas de reseñas tratan tu sitio web como una valla publicitaria para su marca. Te dan un <iframe> rígido o un widget de JavaScript pesado que:
- Reduce tu puntuación de Lighthouse con scripts de terceros.
- Choca con tu sistema de diseño (fuentes incorrectas, colores incorrectos).
- Actúa como un caballo de Troya para dirigir el tráfico de vuelta a su plataforma, no a la tuya.
En Reviewlee, creemos que las reseñas son infraestructura. Proporcionamos la base de datos, los formularios de recolección y el motor de verificación, pero cómo muestras esos datos debería depender de ti.
¿Por qué elegir "Headless"?
Al usar la API de Reviewlee ("Reseñas Headless"), obtienes:
- Cero Desplazamiento de Diseño (Layout Shift): Renderiza reseñas en el servidor (SSR/RSC) para una visualización instantánea.
- Marca Perfecta: Usa tus propias clases de CSS/Tailwind.
- Supremacía SEO: Las reseñas son parte de tu HTML, no inyectadas por JS después de la carga.
Construyamos un componente de reseña personalizado usando Next.js 16 y Tailwind CSS.
Prerrequisitos
- Una cuenta de Reviewlee (Gratis o Pro).
- Tu clave API pública (se encuentra en Configuración > API).
- Un proyecto Next.js (aunque esto funciona con Remix, Astro, etc.).
Paso 1: Obtener Reseñas (Lado del Servidor)
En Next.js App Router, obtenemos datos directamente en nuestro Componente de Servidor. Esto asegura que los motores de búsqueda vean las reseñas inmediatamente.
// lib/api.ts
import { notFound } from "next/navigation";
export interface Review {
id: string;
rating: number;
title: string;
body: string;
author_name: string;
verified: boolean;
created_at: string;
}
export async function getReviews(formId: string) {
const res = await fetch(
`https://api.reviewlee.com/api/v1/reviews?formId=${formId}&status=published`,
{
headers: {
// Usa tu clave pública si obtienes desde el cliente,
// o clave secreta si usas proxy/obtienes desde el servidor.
Authorization: `Bearer ${process.env.REVIEWLEE_API_KEY}`,
},
next: { revalidate: 3600 }, // Caché por 1 hora
}
);
if (!res.ok) {
if (res.status === 404) return [];
throw new Error("Error al obtener las reseñas");
}
const data = await res.json();
return data.reviews as Review[];
}
Paso 2: El Componente de Tarjeta de Reseña
Ahora, construyamos la interfaz de usuario. Usaremos lucide-react para las estrellas y Tailwind para el estilo.
// components/review-card.tsx
import { Star, CheckCircle2 } from "lucide-react";
import { cn } from "@/lib/utils";
import type { Review } from "@/lib/api";
export function ReviewCard({ review }: { review: Review }) {
return (
<div className="p-6 rounded-xl bg-card border border-border shadow-sm">
{/* Encabezado: Autor y Fecha */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<span className="font-semibold text-foreground">{review.author_name}</span>
{review.verified && (
<span className="flex items-center gap-1 text-xs font-medium text-emerald-600 bg-emerald-50 px-2 py-0.5 rounded-full">
<CheckCircle2 className="w-3 h-3" />
Cliente Verificado
</span>
)}
</div>
<time className="text-sm text-muted-foreground">
{new Date(review.created_at).toLocaleDateString("es-ES", {
year: "numeric",
month: "short",
day: "numeric",
})}
</time>
</div>
{/* Clasificación */}
<div className="flex gap-0.5 mb-3" aria-label={`${review.rating} de 5 estrellas`}>
{[1, 2, 3, 4, 5].map((star) => (
<Star
key={star}
className={cn(
"w-4 h-4",
star <= review.rating
? "fill-amber-400 text-amber-400"
: "fill-muted text-muted"
)}
/>
))}
</div>
{/* Contenido */}
<h3 className="font-bold text-lg mb-2">{review.title}</h3>
<p className="text-muted-foreground leading-relaxed">{review.body}</p>
</div>
);
}
Paso 3: Integración y JSON-LD
Finalmente, intégralo en tu página y agrega los Datos Estructurados cruciales para Google.
// app/reviews/page.tsx
import { getReviews } from "@/lib/api";
import { ReviewCard } from "@/components/review-card";
export const metadata = {
title: "Reseñas de Clientes",
description: "Mira lo que nuestros clientes tienen que decir.",
};
export default async function ReviewsPage() {
const reviews = await getReviews("tu-id-de-formulario");
const averageRating =
reviews.reduce((acc, r) => acc + r.rating, 0) / reviews.length;
// Datos de fragmentos enriquecidos de Google
const jsonLd = {
"@context": "https://schema.org",
"@type": "Product",
name: "Plataforma SaaS Acme",
aggregateRating: {
"@type": "AggregateRating",
ratingValue: averageRating.toFixed(1),
reviewCount: reviews.length,
},
};
return (
<section className="container py-12">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<h1 className="text-4xl font-extrabold tracking-tight mb-8">
Amado por más de {reviews.length} desarrolladores
</h1>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{reviews.map((review) => (
<ReviewCard key={review.id} review={review} />
))}
</div>
</section>
);
}
Conclusión
Al tomar el control de la interfaz de usuario, aseguras que tus reseñas generen confianza para ti, no para la plataforma de reseñas.
Este enfoque es:
- Más rápido: Sin JS pesado de terceros.
- Más limpio: Coincide perfectamente con tu marca.
- Más inteligente: Todo el crédito de SEO va a tu dominio.
¿Listo para construir? Obtén tu clave API desde el Panel de Control y comienza a enviar código.