Como usar Rive em React e Next.js
Por equipe Charmotion · Atualizado em

Para usar o Rive no React, instale @rive-app/react-webgl2, chame useRive({ src, stateMachine, autoplay: true }) e renderize o RiveComponent retornado. Troque de estado com useStateMachineInput em arquivos baseados em inputs ou com os hooks de view model em arquivos com data binding. No Next.js, renderize em um client component.
Principais pontos
- O Rive recomenda @rive-app/react-webgl2 para a maioria dos projetos; react-canvas continua funcionando.
- useRive retorna a instância rive e um RiveComponent para renderizar.
- useStateMachineInput controla inputs legados; os hooks de view model controlam arquivos com data binding.
- No Next.js, coloque o Rive em um componente "use client" ou carregue-o com ssr: false.
Qual pacote React do Rive devo instalar?
O Rive publica três pacotes React a partir do repositório rive-react. Eles compartilham a mesma API e diferem na forma de desenhar:
| Pacote | Renderizador | Quando usar |
|---|---|---|
@rive-app/react-webgl2 | Rive Renderer em WebGL2 | Escolha padrão; suporta todos os recursos, incluindo vector feathering |
@rive-app/react-canvas | Canvas2D do navegador | Muitas instâncias em uma página, ou quando o WebGL não está disponível |
@rive-app/react-canvas-lite | Canvas2D, conjunto reduzido de recursos | Menor download quando o arquivo não precisa dos recursos ausentes |
npm install @rive-app/react-webgl2
A documentação web do Rive agora recomenda usar WebGL2 na maioria dos casos. Uma ressalva: os navegadores limitam o número de contextos WebGL por página. Se uma página mostra muitas animações Rive ao mesmo tempo, use o pacote canvas ou passe useOffscreenRenderer: true para que todas as instâncias compartilhem um único contexto offscreen.
O runtime é um módulo WASM, então ele é a maior parte do custo. O Rive informa 567 KB para o WASM do canvas e 648 KB para o build WebGL2 após compressão brotli (janeiro de 2026). Carregue-o apenas nas rotas que precisam dele.
Como renderizar um arquivo .riv com useRive?
Coloque o .riv na sua pasta estática (por exemplo, public/character.riv) e chame useRive. O hook retorna a instância rive, que fica null até o arquivo carregar, e um RiveComponent que renderiza o canvas.
import { useRive } from "@rive-app/react-webgl2";
export function Character() {
const { rive, RiveComponent } = useRive({
src: "/character.riv",
stateMachine: "CharacterStateMachine",
autoplay: true,
});
return (
<div style={{ width: 240, height: 240 }}>
<RiveComponent aria-label="Animated character" />
</div>
);
}
O RiveComponent ocupa todo o elemento pai, então defina o tamanho do wrapper, não do canvas. Dê a ele um rótulo acessível se ele transmitir significado, ou oculte-o das tecnologias assistivas se for puramente decorativo.
Use a opção no singular stateMachine. O runtime web do Rive substituiu o parâmetro no plural stateMachines na v2.41.0, e tutoriais antigos ainda mostram a forma no plural. Se você omitir a state machine, o arquivo toca a animação padrão e seus inputs não vão responder.
Como mudar o estado a partir do React?
A forma de controlar o arquivo depende do que ele expõe.
Arquivos com inputs de state machine (legado)
useStateMachineInput(rive, stateMachineName, inputName) retorna o input assim que o arquivo carrega. Defina .value em números e booleanos; chame .fire() em triggers.
import { useEffect } from "react";
import { useRive, useStateMachineInput } from "@rive-app/react-webgl2";
const SM = "CharacterStateMachine";
export function Character({ mood, successCount }: { mood: number; successCount: number }) {
const { rive, RiveComponent } = useRive({ src: "/character.riv", stateMachine: SM, autoplay: true });
const moodInput = useStateMachineInput(rive, SM, "mood");
const success = useStateMachineInput(rive, SM, "success");
useEffect(() => {
if (moodInput) moodInput.value = mood;
}, [moodInput, mood]);
// Fire the one-shot whenever the parent bumps successCount.
useEffect(() => {
if (successCount > 0) success?.fire();
}, [success, successCount]);
return (
<div style={{ width: 240, height: 240 }}>
<RiveComponent />
</div>
);
}
Arquivos com view models (Data Binding)
Passe autoBind: true e depois use os hooks de view model com rive?.viewModelInstance:
const { rive, RiveComponent } = useRive({ src: "/character.riv", stateMachine: SM, autoplay: true, autoBind: true });
const vmi = rive?.viewModelInstance;
const { value: mood, setValue: setMood } = useViewModelInstanceNumber("mood", vmi);
const { trigger: celebrate } = useViewModelInstanceTrigger("success", vmi);
Você também pode disparar um trigger diretamente de um handler de evento, como o handler de clique de um botão, com success?.fire().
O Rive recomenda Data Binding para projetos novos, mas arquivos baseados em inputs continuam funcionando no React. Use a API que corresponde ao arquivo.
Os dois estilos de hook não retornam nada útil até o arquivo carregar, então proteja cada chamada com optional chaining. Controle os valores a partir de props ou state, como no useEffect acima, em vez de chamar o runtime durante a renderização. Assim a animação fica sincronizada com o estado da sua UI e fica fácil testar o componente com props simples.
Como usar o Rive no App Router do Next.js?
O Rive desenha em um canvas no navegador, então ele precisa rodar em um client component. No App Router, os componentes são server components por padrão. Marque o arquivo que chama useRive com "use client" no topo:
"use client";
import { useRive } from "@rive-app/react-webgl2";
export default function HeroCharacter() {
const { RiveComponent } = useRive({
src: "/character.riv",
stateMachine: "CharacterStateMachine",
autoplay: true,
});
return <div className="h-60 w-60"><RiveComponent /></div>;
}
Depois, importe-o em uma página de servidor normalmente. A página continua sendo renderizada no servidor; só esse componente faz a hidratação no navegador.
Mantenha o .riv em public/ para que ele seja servido como asset estático com uma URL estável. Reserve o tamanho da caixa no CSS para o layout não se deslocar quando o canvas aparecer. Em uma animação na seção hero, uma imagem estática de poster atrás do canvas evita uma caixa vazia durante o carregamento.
No Pages Router vale a mesma regra: o Rive só pode rodar no navegador. Use o padrão de importação dinâmica da próxima seção se aparecerem erros no servidor.
Como corrigir "window is not defined"?
O erro significa que um código que depende de globais do navegador rodou durante a renderização no servidor. Com o Rive, isso costuma acontecer quando o componente é renderizado por um server component ou quando o pacote do Rive é importado em um módulo que o servidor avalia.
Soluções, em ordem de preferência:
- Confira se o componente que importa o Rive começa com
"use client". - Se o erro continuar, carregue o componente apenas no navegador com
next/dynamic:
"use client";
import dynamic from "next/dynamic";
const HeroCharacter = dynamic(() => import("./HeroCharacter"), {
ssr: false,
loading: () => <img src="/character-poster.png" alt="" width={240} height={240} />,
});
Nas versões recentes do Next.js, ssr: false só é permitido dentro de um client component, por isso o wrapper acima também está marcado com "use client".
- Nunca acesse
window,documentou a instância do Rive no nível superior do módulo. Faça isso dentro deuseEffectou de handlers de evento, que só rodam no navegador.
O lazy loading (carregamento sob demanda) com ssr: false também deixa o runtime WASM fora do bundle inicial, o que ajuda em páginas onde a animação fica abaixo da dobra.
Como respeitar o prefers-reduced-motion?
Os usuários podem pedir ao sistema operacional para reduzir o movimento, e os navegadores expõem isso pela media query prefers-reduced-motion, amplamente disponível desde 2020. Respeite essa preferência em qualquer personagem em loop.
"use client";
import { useEffect, useState } from "react";
function usePrefersReducedMotion() {
const [reduce, setReduce] = useState(false);
useEffect(() => {
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
setReduce(mq.matches);
const onChange = () => setReduce(mq.matches);
mq.addEventListener("change", onChange);
return () => mq.removeEventListener("change", onChange);
}, []);
return reduce;
}
export function MaybeAnimated() {
const reduce = usePrefersReducedMotion();
return reduce
? <img src="/character-poster.png" alt="" width={240} height={240} />
: <Character mood={0} successCount={0} />;
}
Renderizar uma imagem estática para movimento reduzido é a opção mais segura, e ainda evita o download do runtime. Se o personagem comunica um estado, como carregamento, mantenha esse significado em texto ou em uma mensagem de status acessível. Veja animação acessível e movimento reduzido para a parte de WCAG.
Adicione uma animação Rive a um app React ou Next.js
- Instale o runtime. Rode
npm install @rive-app/react-webgl2(ou@rive-app/react-canvaspara muitas instâncias em uma página). - Adicione o arquivo. Coloque o .riv em
public/para que ele seja servido em uma URL estável, como/character.riv. - Crie um client component. Comece o arquivo com
"use client"e chameuseRive({ src, stateMachine, autoplay: true }). - Renderize e defina o tamanho. Renderize o
RiveComponentdentro de um wrapper com largura e altura fixas. - Controle a state machine. Use
useStateMachineInputem arquivos baseados em inputs, ouautoBind: truecom os hooks de view model em arquivos com Data Binding. - Trate o movimento reduzido. Verifique
prefers-reduced-motione, quando o valor forreduce, renderize no lugar uma imagem estática (poster).
Perguntas frequentes
Devo usar @rive-app/react-canvas ou @rive-app/react-webgl2?
Use @rive-app/react-webgl2 na maioria dos projetos; o Rive recomenda esse pacote e ele suporta recursos como vector feathering. Escolha @rive-app/react-canvas quando uma página mostra muitas animações ao mesmo tempo e você quer evitar os limites de contextos WebGL, ou quando o WebGL não está disponível. Os dois expõem a mesma API.
Por que meu input do Rive está null no React?
useStateMachineInput retorna null até o arquivo carregar e a state machine estar rodando. Proteja as chamadas com optional chaining, passe exatamente o mesmo nome da state machine para useRive e para o hook, e confira maiúsculas e espaços nos nomes dos inputs. Arquivos convertidos para view models não têm mais inputs.
O Rive funciona com React Server Components?
Componentes do Rive precisam rodar no navegador, então não podem ser server components. Coloque o código do Rive em um componente marcado com "use client" e importe-o em um server component. O servidor renderiza a página ao redor, enquanto o componente do Rive faz a hidratação e desenha no cliente.
Posso usar stateMachines em vez de stateMachine?
Código antigo usa a opção no plural stateMachines, que o Rive descontinuou na v2.41.0 do runtime web em favor de stateMachine, no singular. O código antigo ainda pode funcionar, mas código novo deve usar stateMachine para seguir a documentação atual e evitar quebras futuras quando as opções descontinuadas forem removidas.
Respostas rápidas deste guia
Fontes
- React runtime, Rive
- React runtime: Inputs (legacy), Rive
- Canvas vs WebGL2, Rive
- Web runtime: State machines, Rive
- Runtime sizes, Rive
- Data binding migration guide, Rive
- prefers-reduced-motion, MDN
- Runtimes: Getting started, Rive