Cómo crear una timeline de historia de empresa en React
Una timeline de empresa en React muestra una línea central vertical con tarjetas de hitos alternadas que aparecen al hacer scroll mediante whileInView de Framer Motion, con un retardo escalonado basado en el índice del elemento. En móvil, el diseño se repliega en una única columna alineada a la izquierda.
- Stack: React + Framer Motion + Tailwind v4, ~128 líneas, cero dependencias adicionales.
- Animación: fade-up al hacer scroll (opacity 0→1, y 24→0) con un retardo escalonado de 80 ms por elemento.
- Responsive: diseño alternado de dos columnas en md+, única columna alineada a la izquierda en móvil.
- Accesible: la línea vertical y el dot decorativos llevan aria-hidden; jerarquía semántica h2/h3.
- Personalizable mediante CSS custom properties, ningún color codificado de forma fija.
About Timeline es una sección de hitos apilados verticalmente que visualiza la historia de una empresa o el recorrido de un producto. Cada hito aparece con un fundido al hacer scroll, alternando izquierda y derecha en desktop para romper la monotonía de una lista plana. Combina de forma natural con una sección de equipo o fundadores más abajo en la página.
Anatomía
La sección centra un contenedor max-w-4xl con un bloque de encabezado (badge opcional, título h2, párrafo de subtítulo) y luego el cuerpo de la timeline. Este cuerpo es un div relative: una regla vertical de 1px recorre toda su altura (posicionada de forma absoluta en left-4 en móvil, left-1/2 en desktop). Dentro, las filas de hitos alternan mediante un flag isLeft (índice par = tarjeta a la izquierda, impar = derecha). Cada fila contiene un dot de acento centrado sobre la regla y una tarjeta de texto limitada al 50% de anchura en desktop.
Cómo funciona
Cada tarjeta de hito es un motion.div con initial={{ opacity: 0, y: 24 }} y whileInView={{ opacity: 1, y: 0 }}. El prop viewport usa once: true para que la animación solo se dispare una vez, y margin: '-60px' para que la tarjeta empiece a animarse justo antes de ser plenamente visible. El retardo de transición es i * 0.08 segundos, así los elementos que entran al viewport al mismo tiempo igual se encadenan de forma legible. El bloque de encabezado usa una variante algo más simple (y: 20, sin retardo) para asentarse antes de que entre el cuerpo de la timeline.
Cómo crearlo en React
Define la forma de tus datos de hitos
Empieza con una interface tipada para cada hito: una cadena year, un title y una description. Mantén el array en mock.ts para que el componente siga siendo puramente presentacional. Pásalo como prop obligatorio llamado milestones.
interface MilestoneItem { year: string; title: string; description: string; }Dibuja la regla vertical
Envuelve la lista de hitos en un div relative, luego coloca un div de 1px posicionado de forma absoluta de top-0 a bottom-0. Usa left-4 por defecto y md:left-1/2 para que se desplace al centro en desktop. Márcalo con aria-hidden para que los lectores de pantalla lo ignoren.
<div className="absolute left-4 top-0 bottom-0 w-px md:left-1/2" style={{ backgroundColor: "var(--color-border)" }} aria-hidden />Alterna las tarjetas y añade el dot
Para cada hito, calcula isLeft = i % 2 === 0. El div de la tarjeta recibe md:w-[calc(50%-2rem)] más md:mr-auto md:text-right md:pr-8 (lado izquierdo) o md:ml-auto md:pl-8 (lado derecho). El dot es un círculo posicionado de forma absoluta en left-2 en móvil, md:left-1/2 md:-translate-x-1/2 en desktop, por encima de la regla gracias a z-10.
const isLeft = i % 2 === 0; // card className={`md:w-[calc(50%-2rem)] ${ isLeft ? "md:mr-auto md:text-right md:pr-8" : "md:ml-auto md:pl-8" }`}Conecta las animaciones al scroll
Envuelve cada fila de hito en un motion.div. Ajusta initial a opacity 0 e y 24, whileInView a opacity 1 e y 0, luego pasa viewport={{ once: true, margin: '-60px' }}. Calcula el retardo como i * 0.08 para que los elementos se escalonen de forma natural incluso cuando entran juntos al viewport.
<motion.div initial={{ opacity: 0, y: 24 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, margin: "-60px" }} transition={{ delay: i * 0.08, duration: 0.5, ease: [0.16, 1, 0.3, 1] }} >
Cuándo usarlo
Úsala en páginas Acerca de, secciones de historia de marca o relatos de lanzamiento de producto donde quieras transmitir un crecimiento a lo largo del tiempo. Funciona para cualquier empresa con cuatro hitos o más; por debajo de eso, una lista con viñetas se lee más rápido. Evítala en páginas centradas en conversión donde el desplazamiento compite con un CTA, y descarta la disposición alternada en timelines con mucha densidad de datos donde la legibilidad prima sobre el ritmo visual.
Usado por
- Stripe, Usa una timeline de hitos vertical en su página Acerca de para narrar la historia de la empresa año tras año.
- Notion, Presenta la historia de su fundación como una timeline desplazable con bloques de texto alternados.
- Linear, Se apoya en un diseño de hitos apilado y limpio para comunicar su hoja de ruta y su historia.
FAQ
¿Cómo controlo la velocidad del escalonado entre hitos?
Ajusta el multiplicador en el cálculo del retardo: delay: i * 0.08. Un valor más pequeño como 0.05 aprieta la secuencia; 0.12 la extiende más. Si los hitos entran al viewport uno a uno por la longitud de la página, el retardo apenas importa, solo cuentan once: true y el margin offset.
¿Puedo hacer que la línea vertical se anime mientras el usuario hace scroll?
Sí. Reemplaza el div estático por un motion.div y anima su scaleY de 0 a 1 con useScroll y useTransform, ajustando transformOrigin a top. Esto crea una línea que se dibuja a sí misma a medida que el usuario se desplaza por la sección.
¿La disposición alternada se rompe en pantallas muy estrechas?
No, por debajo del breakpoint md la disposición cambia a una única columna alineada a la izquierda (pl-12 en cada fila, con el dot y la regla fijados a la izquierda en left-4). Las clases de alternancia solo se aplican a partir de md.
¿Cuántos hitos son demasiados?
Más allá de doce elementos, la sección se vuelve agotadora de desplazar. Si tienes más datos, agrúpalos por década u opta mejor por una timeline desplazable horizontal. Entre seis y ocho hitos es un rango cómodo para una sección Acerca de.