Como adicionar animação a um app React ou Next.js
Por equipe Charmotion · Atualizado em

Para adicionar animação no React, instale motion e importe motion de "motion/react" para transições de componentes, use CSS para efeitos simples, DotLottieReact para arquivos Lottie e useRive para um personagem interativo. No Next.js, todo componente que anima no navegador precisa ser um client component.
Principais pontos
- O Framer Motion agora se chama Motion:
npm install motione depois importe de"motion/react". AnimatePresencepermite que os elementos façam a animação de saída antes que o React os remova.<MotionConfig reducedMotion="user">respeita a configuração de movimento reduzido do visitante.- Use
@lottiefiles/dotlottie-reactpara Lottie euseRivepara personagens controlados por estado. - No App Router, comece os componentes animados com
"use client".
Já tem um personagem ou logo? Veja seu personagem em telas reais de app: telas de splash, onboarding, carregamento e estado vazio. É grátis, roda no navegador e não envia nada.
O Framer Motion agora se chama Motion?
Sim. A biblioteca antes publicada como framer-motion agora é o Motion, um projeto independente. O guia de atualização é curto: desinstale o pacote antigo, instale motion e mude o caminho de importação.
npm uninstall framer-motion
npm install motion
// before
import { motion } from "framer-motion";
// after
import { motion } from "motion/react";
A API dos componentes é a mesma, então o código existente com motion.div continua funcionando depois da troca de importação. Tutoriais escritos antes da mudança de nome continuam valendo; é só ler framer-motion como motion/react.
O Motion é uma boa escolha padrão para animação de interface no React: elementos entrando e saindo, mudanças de layout, gestos e transições de elementos compartilhados. Não é a ferramenta certa para uma ilustração desenhada ou um mascote, que vêm de uma ferramenta de design como arquivo. Para esses casos, use Lottie ou Rive, explicados abaixo.

Como animar um componente com o Motion?
Troque um elemento pelo equivalente motion e descreva os estados inicial e final. Envolva elementos condicionais em AnimatePresence para que possam tocar uma animação de saída antes de serem desmontados.
import { AnimatePresence, MotionConfig, motion } from "motion/react";
export function Toast({ open, message }: { open: boolean; message: string }) {
return (
<MotionConfig reducedMotion="user">
<AnimatePresence>
{open && (
<motion.div
key="toast"
role="status"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 12 }}
transition={{ duration: 0.2 }}
>
{message}
</motion.div>
)}
</AnimatePresence>
</MotionConfig>
);
}
Cada filho de AnimatePresence precisa de uma key estável. Com reducedMotion="user", o Motion desativa animações de transform e de layout quando o visitante ativou o movimento reduzido, enquanto mudanças de opacidade e cor continuam rodando. Normalmente é esse o comportamento que você quer. Para um controle mais fino, o hook useReducedMotion() retorna um booleano que você pode usar em condições. Coloque o MotionConfig uma única vez perto da raiz do app, em vez de em cada componente.
Quando CSS puro basta no React?
Muitas vezes. Efeitos de hover, anéis de foco, spinners e fades simples não precisam de biblioteca. Um CSS module ou uma classe Tailwind com transição não custa nada de JavaScript e funciona até antes da hidratação do React.
/* Button.module.css */
.button { transition: transform 150ms ease-out; }
.button:active { transform: scale(0.97); }
@media (prefers-reduced-motion: reduce) {
.button { transition: none; }
}
Recorra ao Motion quando o CSS ficar complicado:
- um elemento precisa animar a saída antes que o React o remova;
- uma mudança de layout (reordenar lista, card que expande) deve ser animada de forma suave;
- os valores vêm do estado, de gestos ou da posição de rolagem;
- várias animações precisam rodar em sequência.
Uma divisão sensata é CSS para microinterações, Motion para a coreografia dos componentes e um arquivo de animação para tudo que um ilustrador desenhou. Manter os três separados também deixa seu bundle pequeno: páginas que só usam CSS nunca carregam o Motion.
Como adicionar um arquivo Lottie no React?
Instale o player React da LottieFiles e renderize-o com o caminho de um arquivo .lottie ou .json:
npm install @lottiefiles/dotlottie-react
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
export function EmptyState() {
return (
<DotLottieReact
src="/animations/empty-inbox.lottie"
loop
autoplay
style={{ width: 240, height: 240 }}
/>
);
}
DotLottieReact renderiza um canvas e aceita props de canvas como style e className. Para controles de reprodução personalizados, a prop dotLottieRefCallback entrega a instância do player por baixo, que tem métodos de play, pause e seek. O componente vem do mesmo projeto dotlottie-web que o player web puro, então os arquivos se comportam igual no React e em páginas estáticas.
O Lottie funciona bem para ilustrações que tocam do início ao fim: estados vazios, cenas de onboarding, confete. Se a animação precisa reagir ao estado do app, como trocar de loading para success, considere um arquivo construído em torno de uma state machine.
Como adicionar um personagem animado com estados?
Use o runtime React do Rive. useRive carrega o arquivo e retorna um RiveComponent para renderizar; com autoBind: true, os hooks de view model fornecem setters para as propriedades do arquivo. O Rive recomenda @rive-app/react-webgl2 para a maioria dos projetos, e @rive-app/react-canvas tem a mesma API.
import { useEffect } from "react";
import {
useRive,
useViewModelInstanceNumber,
useViewModelInstanceTrigger,
} from "@rive-app/react-canvas";
type Status = "idle" | "loading" | "done";
export function Mascot({ status }: { status: Status }) {
const { rive, RiveComponent } = useRive({
src: "/mascot.riv",
stateMachine: "CharacterViewModelMachine",
autoplay: true,
autoBind: true,
});
const vmi = rive?.viewModelInstance;
const { setValue: setMood } = useViewModelInstanceNumber("mood", vmi);
const { trigger: success } = useViewModelInstanceTrigger("success", vmi);
useEffect(() => {
if (status === "loading") setMood?.(5);
if (status === "done") { success?.(); setMood?.(0); }
}, [status, setMood, success]);
return (
<div style={{ width: 200, height: 200 }}>
<RiveComponent aria-label="Assistant mascot" />
</div>
);
}
Controle o personagem a partir de props, nunca durante a renderização, e defina o tamanho do wrapper em vez do canvas. O Charmotion gera esse tipo de arquivo a partir de um personagem, mascote ou logo enviado, junto com código React pronto; o guia de Rive no React cobre em detalhe a API baseada em inputs e todas as opções de pacote.
Como evitar problemas de SSR e hidratação no Next.js?
No App Router, os componentes são server components por padrão, e server components não podem usar hooks, APIs do navegador nem event handlers. Todo arquivo que chama useRive, renderiza DotLottieReact ou usa Motion com estado deve começar com a diretiva "use client":
"use client";
import { Mascot } from "./Mascot";
export default Mascot;
Mantenha a fronteira do cliente pequena. Faça do widget animado um client component e importe-o numa página que, no resto, é renderizada no servidor, para que só esse widget envie JavaScript.
Se uma biblioteca acessa window no momento da importação e ainda falha durante a pré-renderização, carregue-a só no navegador com next/dynamic:
"use client";
import dynamic from "next/dynamic";
const Mascot = dynamic(() => import("./Mascot").then((m) => m.Mascot), {
ssr: false,
loading: () => <div style={{ width: 200, height: 200 }} />,
});
O placeholder evita que o layout se desloque enquanto o runtime carrega. No Pages Router vale o mesmo padrão dynamic(..., { ssr: false }), sem a diretiva.
Como adicionar um personagem animado a uma página Next.js
- Instale o runtime. Rode
npm install @rive-app/react-canvas(ou@rive-app/react-webgl2) e coloque o .riv empublic/. - Crie um client component. Comece o arquivo com
"use client"e chameuseRivecom o arquivo, o nome da state machine,autoplayeautoBind: true. - Obtenha os setters. Use
useViewModelInstanceNumberpara o humor euseViewModelInstanceTriggerpara reações únicas. - Ligue o estado do app ao personagem. Num
useEffect, defina o humor ou dispare um trigger sempre que a prop de status mudar. - Renderize e defina o tamanho. Renderize o
RiveComponentdentro de um wrapper com largura e altura fixas e importe o componente na sua página de servidor.
Perguntas frequentes
Ainda devo instalar o framer-motion?
Não. Projetos novos devem instalar motion e importar de "motion/react". Projetos existentes podem migrar desinstalando framer-motion, instalando motion e atualizando os caminhos de importação; o guia de atualização do Motion mostra que a API dos componentes não mudou, então o código existente com motion.div continua funcionando depois da atualização das importações.
Por que aparece "window is not defined" no Next.js?
Uma biblioteca que só roda no navegador foi executada no servidor. Adicione "use client" ao componente que a usa e, se o erro continuar, importe esse componente com next/dynamic e ssr: false para que ele carregue só no navegador. Dê ao import dinâmico um placeholder com tamanho definido para o layout não se deslocar.
Lottie ou Rive para um app React?
Use Lottie para ilustrações que tocam do início ao fim, como estados vazios ou cenas de onboarding. Use Rive quando um personagem precisa trocar de estado em resposta a eventos do app, porque um único arquivo com state machine substitui várias animações separadas e a lógica de troca entre elas. Muitos apps usam os dois: Lottie para cenas, Rive para o mascote.
Como respeitar o movimento reduzido no React?
Envolva seu app em <MotionConfig reducedMotion="user"> para as animações do Motion, adicione media queries prefers-reduced-motion ao CSS e use useReducedMotion() ou matchMedia para pausar os players de Lottie e Rive ou mostrar um frame estático. Para testar, ative a opção de reduzir movimento no seu sistema operacional.
Respostas rápidas deste guia
Fontes
- Upgrade guide: framer-motion to motion, Motion
- React accessibility: reduced motion, Motion
- dotlottie-web: players for web, React, Vue, Svelte and web components, LottieFiles on GitHub
- React runtime, Rive
- Canvas vs WebGL, Rive
- use client directive, Next.js
- prefers-reduced-motion, MDN Web Docs