Contexto
Necesitaba un componente que rota entre dos labels con animacion de fade-out/swap/fade-in. El reflejo es instalar framer-motion, pero para una animacion tan simple es overkill (~30kb).
Lo que aprendi
useState + setInterval + CSS transition resuelven el 90% de las animaciones simples sin dependencias externas.
"use client";
import { useEffect, useState } from "react";
const labels = [
"Full-Stack + AI Agents",
"sistemas autonomos en produccion",
];
export function RotatingLabel() {
const [index, setIndex] = useState(0);
const [visible, setVisible] = useState(true);
useEffect(() => {
const interval = setInterval(() => {
// 1. Fade out
setVisible(false);
// 2. Swap text after transition completes (350ms)
setTimeout(() => {
setIndex((i) => (i + 1) % labels.length);
// 3. Fade in
setVisible(true);
}, 350);
}, 4000);
return () => clearInterval(interval);
}, []);
return (
<span
className="inline-block transition-all duration-350"
style={{
opacity: visible ? 1 : 0,
transform: visible ? "translateY(0)" : "translateY(4px)",
}}
>
{labels[index]}
</span>
);
}
Desglose de la secuencia
t=0s visible=true "Full-Stack + AI Agents" (opaco)
t=4s visible=false fade out (350ms CSS transition)
t=4.35s index=1 swap text + visible=true, fade in
t=8s visible=false fade out again
t=8.35s index=0 swap back + fade in
...
Cuando SI usar framer-motion
- Animaciones con spring physics (bounce, overshoot)
- Shared layout animations (morph entre elementos)
- Gesture-driven animations (drag, pinch)
- Exit animations (
AnimatePresence)
Cuando CSS transitions bastan
- Fade in/out
- Slide in/out
- Scale up/down
- Cualquier combinacion de
opacity,transform,color
Para el caso de RotatingLabel, son 2 propiedades CSS (opacity + translateY). No necesita 30kb de JavaScript.