Motion (framer-motion) en React: las 6 props que resuelven el 90% de tus animaciones

animate, initial, whileInView, transition, variants y layout: las 6 props de Motion para React (v12) con las que resolvés casi todas las animaciones de una web real. Con demo interactiva tocable.

Motion (framer-motion) en React: las 6 props que resuelven el 90% de tus animaciones

La escena es de todas las semanas: un componente que debe entrar con suavidad, una lista que aparece en cascada, una tarjeta que reordena el layout. Y el desarrollador tiene tres opciones: escribir lógica de animación a mano, instalar una librería distinta para cada efecto, o usar una sola librería de la que solo conoce una parte del API. La mayoría elige la tercera, pero se queda con el 10% del poder.

Motion (el paquete motion, ex framer-motion, hoy en su versión 12) resuelve las animaciones en React con props declarativas sobre componentes. Y la buena noticia: seis props cubren la abrumadora mayoría de los casos que aparecen en una web real — entradas de elementos, reveals al hacer scroll, easing, cascadas y reordenamientos de layout. Este artículo recorre cada una con código real y una demo que podés tocar.

animate e initial: el estado de partida y el estado visible

animate define el estado final del elemento: la posición, opacidad o escala que debe tener cuando la animación termina. initial define el punto de partida, y solo tiene sentido si existe un animate al que llegar.

tsx
import { motion } from 'motion/react';

<motion.div
  initial={{ opacity: 0, y: 16 }}
  animate={{ opacity: 1, y: 0 }}
/>

Ese es el patrón de entrada más común de la web: el elemento arranca invisible y corrido 16 píxeles hacia abajo, y entra en su lugar al montarse. Sin initial, el componente renderiza directamente en el estado final: no hay animación de entrada.

Un detalle que ahorra bugs: cuando el estado cambia de forma dinámica —una caja que se mueve a otra posición por un toggle, por ejemplo— animate también reacciona. La prop no solo anima la entrada: anima la transición entre cualquier par de valores. Si querés reproducir la entrada de nuevo, la forma más simple es remontar el elemento con una key distinta.

La demo: tocá las seis props en acción

Cada celda de la demo muestra una prop con su micro-ejemplo. Mové la caja con animate, reproducí la entrada con initial, scrolleá hasta la caja verde para disparar whileInView, corré la carrera de easing con transition, reiniciá la cascada de variants y reordená el layout con layout. Todas respetan prefers-reduced-motion: si tu sistema lo pide, las animaciones se aplican sin duración.

Las 6 props de Motion, una por celdaDemo interactiva

Tocá cada botón para ver la prop en acción. Librería: motion v12 (ex framer-motion). Las animaciones respetan prefers-reduced-motion.

animateanimate={{ x: 88 }}
initialinitial → animate
whileInViewviewport={{ once: true }}

Scrolleá hasta la caja verde:

Contenido de relleno para que la caja arranque fuera de vista.

transitionease: easeOut
variantsstaggerChildren
layoutlayout (FLIP)

whileInView y transition: animar cuando el usuario te ve, con el ritmo justo

whileInView dispara la animación cuando el elemento entra en la pantalla — la base de los reveals que ves en casi todas las landings modernas. La prop se acompaña con viewport para configurar cuándo cuenta como “visible”: once: true para animar una sola vez, amount para exigir un porcentaje mínimo de visibilidad.

tsx
<motion.div
  initial={{ opacity: 0, x: -20 }}
  whileInView={{ opacity: 1, x: 0 }}
  viewport={{ once: true, amount: 0.4 }}
/>

transition controla cómo se mueve entre estados: duración, delay y —lo que más cambia la percepción— el easing. La regla que vale oro: nunca usar linear para entradas. Una animación que arranca a velocidad constante se siente robótica; la curva estándar [0.22, 1, 0.36, 1] (easeOut expo) arranca rápido y decelera, que es lo que el ojo espera de un movimiento natural.

tsx
<motion.div
  animate={{ x: 100 }}
  transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
/>

En la celda transition de la demo, el naranja corre con linear y el rojo con easeOut: misma distancia, misma duración, percepción completamente distinta.

variants y layout: cascadas y reordenamientos sin lógica de animación

variants agrupa estados con nombre —hidden, show— y los propaga de un componente padre a sus hijos. Es la forma correcta de animar listas y grids en cascada, porque el padre controla el tempo de todos los hijos con staggerChildren:

tsx
const list = {
  hidden: {},
  show: { transition: { staggerChildren: 0.12 } },
};
const item = {
  hidden: { opacity: 0, y: 12 },
  show: { opacity: 1, y: 0 },
};

<motion.div variants={list} initial="hidden" animate="show">
  {items.map((i) => (
    <motion.div key={i.id} variants={item} />
  ))}
</motion.div>

El padre dice “show”, y cada hijo ejecuta su variante con 120 ms de diferencia. Sin la prop, habría que calcular delays a mano para cada ítem.

layout es la que sorprende: marca el elemento para que Motion detecte cambios de posición, tamaño o reordenamiento en el DOM y los anime con la técnica FLIP — sin escribir una línea de lógica. Cambiás la dirección de un flex, quitás un ítem de una lista, y la animación de transición surge sola. El costo es real en elementos muy grandes —que Motion tenga que medir y animar layout siempre cuesta—, por eso va en elementos acotados, no en contenedores con cientos de nodos.

El orden de aprendizaje

animate e initial primero: con ellos resolvés entradas y cambios de estado. Después transition para que se sienta profesional. whileInView cuando quieras reveals al scroll, variants para cascadas y layout para reordenamientos. Ese es el camino que usamos en Gambit Lab para incorporar Motion en cada proyecto de React: empieza chico y sumá props cuando el caso lo pida.

El criterio de rendimiento y la regla de reduced motion

Seis props no son una excusa para animar todo. La regla que aplicamos: cada animación debe tener un propósito perceptible —guiar la atención, dar feedback, comunicar estructura— y debe costar poco. Preferí animar opacity y transform, que el navegador compone en la GPU, antes que propiedades como width, height o top que fuerzan layout en cada frame.

Y el estándar que ninguna animación debería saltarse: useReducedMotion() de la propia librería. Con un true, reducís las duraciones a cero y el contenido queda intacto — exactamente lo que hace la demo de este artículo. La accesibilidad no es un extra: es la condición para que la animación sea una mejora y no una barrera para quien la sufre.

Probalo ahora: tocá cada celda de la demo, mirá la diferencia del easing en la carrera y luego prendé “reducir movimiento” en tu sistema operativo — todo sigue funcionando, solo sin movimiento. Si te quedaste con ganas de animaciones vinculadas al scroll, el tutorial de scroll-driven animations con CSS nativo y GSAP es el paso siguiente: la comparación honesta entre la API nativa y la librería cuando el caso deja de ser “una prop de Motion” y pasa a ser “el scroll controla la animación”.

¿Tienes un proyecto en mente?

Si estás trabajando en algo parecido y querés contrastar ideas, escribinos.

Compartir: