---
title: "Carrusel de testimonios en React con scroll snap nativo"
description: "Carrusel de testimonios en React sin biblioteca de slider: scroll-snap centra cada tarjeta y una animación CSS abre el resto en abanico. Puntos, flechas y fuente."
canonical: "https://react-horizontal-scrolling-menu.dev/es/testimonial-carousel"
image: "https://react-horizontal-scrolling-menu.dev/og.png"
---

# Un carrusel de testimonios en React, sobre scroll snap nativo

La sección de reseñas de todo sitio de marketing: una tarjeta en el centro, sus vecinas inclinadas detrás, puntos debajo, un deslizamiento o un clic para pasar a la siguiente. Las bibliotecas de slider venden exactamente esto. El navegador ya tiene las dos mitades — `scroll-snap-type` para el aterrizaje y una animación dirigida por el desplazamiento para la inclinación — y esta biblioteca añade la parte que no es ninguna de las dos: saber qué tarjeta es la actual.

M**Mia Chen**Product designer

“Setup took an afternoon and the first report landed in my inbox the next morning. I have not opened the old spreadsheet since.”

T**Tomás Reyes**Engineering manager

“The weekly digest is the only email my team reads end to end. Short, specific, and it names the person who unblocked you.”

A**Aisha Okafor**Founder

“I wanted numbers I could defend in a board meeting. Every figure links back to the source, so nobody has to take my word for it.”

J**Jonas Lindqvist**Data analyst

“Exports are plain CSV with sane column names. That alone saved me a script I had been maintaining for two years.”

P**Priya Natarajan**Operations lead

“Support answered a Sunday question in twenty minutes with a fix, not a ticket number. That is what I pay for.”

L**Lucas Moreau**Freelance developer

“The API does exactly what the docs say, and the docs are one page. I shipped the integration before lunch.”

Desliza, arrastra o usa las flechas y los puntos — la fila se asienta sobre una tarjeta cada vez.

[Edita este ejemplo en vivo en Storybook](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu/?path=/story/examples-scrollsnap--scroll-snap)

## Para qué sirve realmente aquí una biblioteca de slider

Los carruseles de testimonios son la instalación canónica de Swiper: tres tarjetas visibles, la del medio plana, puntos de paginación, autoplay. Cada una de esas cosas es CSS o unas pocas líneas sobre la API de esta biblioteca:

-   **Aterrizar en una tarjeta** es `scroll-snap-type: x mandatory` en la fila y `scroll-snap-align: center` en los elementos. El desplazamiento táctil, con rueda y con teclado sigue siendo nativo y desacelera solo hasta una tarjeta.
-   **La inclinación** es una animación dirigida por el desplazamiento en cada tarjeta, así que el ángulo sigue la posición de scroll píxel a píxel sin medir nada en JavaScript. Chromium y Safari 26 la dibujan; Firefox aún la mantiene tras una bandera y muestra tarjetas planas, y la regla está protegida con `@supports`.
-   **Puntos y flechas** necesitan saber la tarjeta actual: `onScroll` encuentra la más cercana al centro de la fila, y `scrollToItem(el, 'smooth', 'center')` avanza a una vecina o salta a la tarjeta de un punto, cayendo en un punto de ajuste por construcción.

## Lo único que hay que hacer bien: el arrastre

Un contenedor con ajuste obligatorio ajusta cada escritura programática en `scrollLeft`, así que la [receta de arrastre con el ratón](https://react-horizontal-scrolling-menu.dev/es/examples/mouse-drag.md) tal cual iría a trompicones de tarjeta en tarjeta. El [ejemplo de scroll snap](https://react-horizontal-scrolling-menu.dev/es/examples/scroll-snap.md) muestra la solución: apagar el ajuste durante el gesto, deslizarse hasta la tarjeta más cercana con `scrollToItem` al soltar y devolver el ajuste a CSS cuando ese deslizamiento se haya asentado — reactivarlo antes salta en lugar de deslizarse. El tacto no necesita nada de esto; la fila es un contenedor de scroll real.

## Autoplay, bucle y lo que sigue siendo tuyo

El [autoplay](https://react-horizontal-scrolling-menu.dev/es/examples/autoplay.md) es un temporizador que llama al mismo `scrollToItem`, en pausa al pasar el ratón, con el foco y con movimiento reducido. Un [bucle infinito](https://react-horizontal-scrolling-menu.dev/es/examples/infinite-loop.md) también se combina: su teletransporte se mueve una longitud de bucle entera, que es un número entero de puntos de ajuste. Las tarjetas son tus componentes — un avatar, una valoración, una cita, un logo — y la geometría son tres propiedades personalizadas: ancho de tarjeta, separación e inclinación.

## El patrón, en su forma mínima

El ajuste y la inclinación son la hoja de estilos; las flechas y los puntos son una medición y un `scrollToItem`. La demo de arriba es esto más las tarjetas.

TestimonialCarousel.tsx

```
function TestimonialCarousel({ reviews }: { reviews: Review[] }) {
  const [active, setActive] = React.useState(0);

  return (
    <ScrollMenu
      scrollContainerClassName="snap-rail"
      itemClassName="snap-slot"
      onScroll={(api) => setActive(nearestIndex(api, reviews))}
      LeftArrow={<Arrow id={reviews[active - 1]?.id} />}
      RightArrow={<Arrow id={reviews[active + 1]?.id} />}
    >
      {reviews.map((review) => (
        <ReviewCard itemId={review.id} key={review.id} {...review} />
      ))}
    </ScrollMenu>
  );
}

// The card whose center is closest to the rail's center.
function nearestIndex(api: publicApiType, reviews: Review[]) {
  const rail = api.scrollContainer.current!;
  const middle = rail.scrollLeft + rail.clientWidth / 2;
  const distances = reviews.map(({ id }) => {
    const slot = api.getItemElementById(id) as HTMLElement;
    return Math.abs(slot.offsetLeft + slot.offsetWidth / 2 - middle);
  });
  return distances.indexOf(Math.min(...distances));
}

// Arrows step one card: the neighbour of the centered one, centered.
function Arrow({ id }: { id?: string }) {
  const api = React.useContext<publicApiType>(VisibilityContext);
  const step = () => {
    const el = id && api.getItemElementById(id);
    if (el) api.scrollToItem(el, 'smooth', 'center');
  };
  return <button disabled={!id} onClick={step}>→</button>;
}

/* .snap-rail — the browser lands every swipe on a card:
     scroll-snap-type: x mandatory;
     padding-inline: calc(50% - var(--card) / 2);
   .snap-slot — scroll-snap-align: center;
   The fan is the snap.css panel on the homepage. */
```

## O instálalo como componente shadcn

El elemento de registro [snap-carousel](https://react-horizontal-scrolling-menu.dev/r/snap-carousel.json) trae el patrón completo — ajuste, abanico, flechas, puntos y un arrastre que cae sobre la tarjeta más cercana — como componente con estilos Tailwind en tu `components/ui/`. Pon tus propias tarjetas:

shadcn

```
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/snap-carousel.json
```

## Ejemplos relacionados

-   [Carrusel con ajuste de desplazamientoLas tarjetas se ajustan al centro y se abren en abanico con CSS — sin motor de carrusel.](https://react-horizontal-scrolling-menu.dev/es/examples/scroll-snap.md)
-   [AutoplayUn bucle que avanza solo con comportamiento de pausa accesible.](https://react-horizontal-scrolling-menu.dev/es/examples/autoplay.md)
-   [Bucle infinitoBucle continuo desde la API pública — sin cambios en la biblioteca.](https://react-horizontal-scrolling-menu.dev/es/examples/infinite-loop.md)

[Todos los ejemplos (23)](https://react-horizontal-scrolling-menu.dev/es/examples.md)

---

More examples: <https://react-horizontal-scrolling-menu.dev/examples.md>
Library summary for LLMs: <https://react-horizontal-scrolling-menu.dev/llms.txt>
