Retour au catalogue

Before After Carousel

Carrousel de paires avant/apres avec transitions synchronisees. Navigation par fleches et indicateurs.

before-aftercomplex Both Responsive a11y
elegantcorporatebeautyreal-estateagencyuniversalcarousel
Theme

Cómo crear un carrusel antes/después en React

Un carrusel antes/después React asocia cada item con un estado booleano (showAfter) y envuelve la imagen mostrada en un bloque AnimatePresence para que Framer Motion encadene ambos estados con un fundido cruzado. Botones de flecha e indicadores de puntos gestionan la navegación; cada cambio de pareja reinicia showAfter a false, así que toda pareja nueva empieza siempre en la vista de antes.

  • Stack: React 18 + Framer Motion 11 + Lucide React, ~140 líneas, cero dependencias adicionales.
  • API clave de Framer Motion: AnimatePresence mode='wait', motion.div con opacidad 0→1.
  • Admite tanto URLs de imágenes (background-image) como colores sólidos de fallback por pareja.
  • Accesible: cada botón de navegación lleva un aria-label; los botones de puntos nombran su pareja de destino.
  • Responsive por defecto (max-width 800px, aspect-ratio 3/2, tamaño de texto fluido vía clamp).

Before After Carousel es una sección React que permite recorrer un conjunto de parejas de transformación, una a una, y luego alternar entre el estado de antes y de después con un solo botón. Encaja en cualquier sitio donde quieras mostrar resultados secuenciales: revisiones de diseño, proyectos de reforma, progresiones cosméticas. El layout es minimalista para que el contenido se mantenga en primer plano.

Anatomía

La sección tiene un encabezado centrado (título + descripción) sobre un canvas limitado a 800px. Dentro de ese canvas: un área de imagen con aspect-ratio fijo 3:2 y esquinas redondeadas, una insignia de estado fijada arriba a la izquierda que indica 'Antes' o 'Después', una fila de controles debajo (flechas anterior/siguiente, contador de pareja, etiqueta, botón conmutador) y una tira de indicadores de puntos al pie. El punto activo se estira de 8px a 24px de ancho para señalar la posición actual sin depender solo del color.

Cómo funciona

La transición entre antes y después la controla AnimatePresence mode='wait' que envuelve una motion.div cuya clave es `${pair.id}-${showAfter ? 'after' : 'before'}`. Cuando la clave cambia, el frame saliente se desvanece primero (opacidad 0, duración 0,4s) y luego el frame entrante aparece en fundido. Un array de easing personalizado [0.16, 1, 0.3, 1] da a ambos fundidos un inicio rápido y un final suave. La navegación hacia una nueva pareja llama a setShowAfter(false) de forma síncrona para que el cambio de clave y el cambio de estado produzcan un único fundido cruzado limpio en vez de dos consecutivos.

Cómo crearlo en React

  1. Modelar cada pareja con una interface tipada

    Define una interface Pair con un id, un label y URLs opcionales beforeImage/afterImage junto con fallbacks beforeColor/afterColor. Pasa el array como prop para que el componente se mantenga puramente presentacional.

    interface Pair {
      id: string;
      label: string;
      beforeImage?: string;
      afterImage?: string;
      beforeColor?: string;
      afterColor?: string;
    }
  2. Calcular el fondo a partir del estado actual

    Dentro de la motion.div, calcula el fondo en el render: si showAfter es true y afterImage está definido, usa un background-image CSS url(); si no, recurre a afterColor y luego al token accent. La misma lógica se aplica al lado before con beforeImage y beforeColor.

    background: showAfter
      ? (pair.afterImage ? `url(${pair.afterImage}) center/cover` : pair.afterColor ?? "var(--color-accent)")
      : (pair.beforeImage ? `url(${pair.beforeImage}) center/cover` : pair.beforeColor ?? "var(--color-background-alt)"),
  3. Asignar key a AnimatePresence para fundidos cruzados limpios

    Envuelve la motion.div en AnimatePresence mode='wait'. Usa una key compuesta que combine el id de la pareja y la vista actual, para que cualquier cambio de estado, navegación o conmutación, produzca un nuevo ciclo mount/unmount en vez de una animación interrumpida.

    <AnimatePresence mode="wait">
      <motion.div
        key={`${pair.id}-${showAfter ? "after" : "before"}`}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
      />
    </AnimatePresence>
  4. Reiniciar el estado al navegar

    En los handlers prev y next, llama a setShowAfter(false) junto con la actualización del índice. Esto garantiza que la nueva pareja siempre se abra en su vista before, lo que hace que el patrón de revelado parezca intencionado y no aleatorio.

    const prev = () => {
      setCurrent((c) => (c - 1 + pairs.length) % pairs.length);
      setShowAfter(false);
    };

Cuándo usarlo

Este carrusel encaja en landing pages donde tienes dos o más casos de transformación que mostrar, tratamientos de belleza, obras de reforma, antes/después de diseño, portfolios de retoque fotográfico. Funciona mejor con cuatro a ocho parejas; con menos parejas el carrusel queda vacío, y por encima de ocho la tira de puntos se vuelve inmanejable. Evítalo si necesitas ambos estados visibles al mismo tiempo (usa entonces una vista dividida con deslizador), o si todas tus parejas son solo de texto y no hay nada visual que revelar.

Usado por

  • Squarespace, Usa carruseles antes/después en su galería de plantillas para mostrar las transformaciones de diseño de sitios.
  • Lightroom (Adobe), Muestra los resultados de retoque fotográfico con conmutadores antes/después en sus páginas de marketing y de onboarding.
  • Houzz, Muestra parejas de reformas del hogar en una navegación antes/después secuencial a lo largo de sus galerías de proyectos.

FAQ

¿Por qué usar AnimatePresence mode='wait' en lugar de una transición CSS de opacity?

mode='wait' garantiza que el elemento saliente se desvanezca por completo antes de que aparezca el entrante, produciendo un fundido cruzado secuencial limpio. Una transición CSS clásica sobre un render condicional superpondría visualmente ambos estados durante la transición.

¿Puedo usar imágenes reales en lugar de colores de marcador de posición?

Sí. Pasa beforeImage y afterImage como URLs absolutas o relativas en cada objeto Pair. El componente las muestra como background-image CSS con un redimensionado center/cover. Sin URL, recurre a beforeColor o afterColor y luego a los tokens del tema.

¿Cómo cambio el número de items visibles o el comportamiento en bucle?

El componente muestra una pareja a la vez y da la vuelta en módulo en ambos extremos. Para desactivar el bucle, acota el índice: `setCurrent(c => Math.max(0, c - 1))` para prev y `Math.min(pairs.length - 1, c + 1)` para next, y luego oculta las flechas en los límites.

¿Es el componente accesible sin ratón?

Todos los controles interactivos son botones nativos con atributos aria-label, así que la navegación por teclado funciona sin configuración. Cada indicador de punto nombra su pareja de destino. La insignia de estado es texto visible, no una affordance basada solo en icono.

"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";

interface Pair {
  id: string;
  label: string;
  beforeImage?: string;
  afterImage?: string;
  beforeColor?: string;
  afterColor?: string;
}

interface BeforeAfterCarouselProps {
  title?: string;
  description?: string;
  pairs?: Pair[];
}

const EASE = [0.16, 1, 0.3, 1] as const;

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reseñas

Carrusel antes/después React, fundido Framer Motion