
Bringing new instruments right into a workflow is at all times thrilling—curiosity bumps up in opposition to the consolation of acquainted strategies. However when our longtime shopper, Chumbi Valley, got here to us with their Valley Adventures undertaking, we noticed the right alternative to experiment with Rive and craft cartoon-style animations that matched the playful spirit of the model.
Rive is a robust real-time interactive design software with built-in help for interactivity by State Machines. On this information, we’ll stroll you thru how we built-in a .riv file right into a React atmosphere and added mouse-responsive animations.
We’ll additionally stroll by a modernized integration methodology utilizing Rive’s newer Information Binding function—our present most well-liked method for reaching the identical animation with much less complexity and larger flexibility.
Animation Idea & File Preparation
Valley Adventures is a gamified Chumbi NFT staking program, the place magical creatures known as Chumbi inhabit an enchanted world. The visible course leans closely into fairytale e book illustrations—vibrant colours, playful characters, and a whimsical, cartoon-like aesthetic.

To right away immerse customers on this world, we went with a full-section hero animation on the touchdown web page. We cut up the animation into two elements:
- an idle animation that brings the scene to life;
- a cursor-triggered parallax impact, including depth and interactivity.
A number of parts animate concurrently—background layers like rustling leaves and flickering fireflies, together with foreground characters that react to motion. The result’s a dynamic, storybook-like expertise that invitations customers to discover.
Probably the most attention-grabbing—and trickiest—a part of the combination was tying animations to mouse monitoring. Rive supplies a built-in technique to deal with this: by making use of constraints with various strengths to parts inside a gaggle that’s linked to Mouse Monitoring, which itself responds to the cursor’s place.
Nevertheless, we encountered a limitation with this method: the HTML buttons layered above the Rive asset had been blocking the hover state, stopping it from triggering the animation beneath.
To work round this, we used a extra strong methodology that gave us finer management and prevented these issues altogether.
Right here’s how we approached it:
- Create 4 separate timelines, every with a single keyframe representing an excessive place of the animation group:
- Far left
- Far proper
- High
- Backside
- Add two animation layers, every accountable for mixing between reverse keyframes:
- Layer 1 blends the far-left and far-right timelines
- Layer 2 blends the highest and backside timelines
- Tie every layer’s mix quantity to a numeric enter—one for the X axis, one for the Y axis.
By adjusting the values of those inputs based mostly on the cursor’s place, you may management how tightly the animation responds on every axis. This method offers you a smoother, extra customizable parallax impact—and prevents sudden habits attributable to overlapping UI.
As soon as the animation is prepared, merely export it as a .riv file—and go away the remainder of the magic to the devs.
How We Did It: Integrating a Rive File right into a React Challenge
Earlier than we dive additional, let’s make clear what a .riv file truly is.
A .riv file is the export format from the Rive editor. It could actually embrace:
- vector graphics,
- timeline animations,
- a State Machine with enter parameters.
In our case, we’re utilizing a State Machine with two numeric inputs: Axis_X and Axis_Y. These inputs are tied to how we management animation in Rive, utilizing values from the X and Y axes of the cursor’s place.
These inputs drive the motion of various parts—just like the swaying leaves, fluttering fireflies, and even refined character reactions—making a clean, interactive expertise that responds to the person’s mouse.
Step-by-Step Integration
Step 1: Set up the Rive React runtime
Set up the official package deal:
npm set up @rive-app/react-canvas
Step 2: Create an Animation Element
Create a element known as RiveBackground.tsx to deal with loading and rendering the animation.
Step 3: Join animation
const { rive, setCanvasRef, setContainerRef } = useRive({
src: 'https://cdn.rive.app/animations/hero.riv',
autoplay: true,
format: new Format({ match: Match.Cowl, alignment: Alignment.Heart }),
onLoad: () => setIsLoaded(true),
enableRiveAssetCDN: true,
});
For a greater understanding, let’s take a more in-depth take a look at every prop you’ll usually use when working with Rive in React:
What every possibility does:
Property | Description |
src | Path to your .riv file — will be native or hosted through CDN |
autoplay | Mechanically begins the animation as soon as it’s loaded |
format | Controls how the animation matches into the canvas (we’re utilizing Cowl and Heart) |
onLoad | Callback that fires when the animation is prepared — helpful for setting isLoaded |
enableRiveAssetCDN | Permits loading of exterior belongings (like fonts or textures) from Rive’s CDN |
Step 4: Join State Machine Inputs
const numX = useStateMachineInput(rive, 'State Machine 1', 'Axis_X', 0);
const numY = useStateMachineInput(rive, 'State Machine 1', 'Axis_Y', 0);
This setup connects on to the enter values outlined contained in the State Machine, permitting us to replace them dynamically in response to person interplay.
- State Machine 1 — the identify of your State Machine, precisely as outlined within the Rive editor
- Axis_X and Axis_Y — numeric inputs that management motion based mostly on cursor place
- 0 — the preliminary (default) worth for every enter
☝️ Necessary: Be certain your .riv file consists of the precise names: Axis_X, Axis_Y, and State Machine 1. These should match what’s outlined within the Rive editor — in any other case, the animation received’t reply as anticipated.
Step 5: Deal with Mouse Motion
useEffect(() => {
if (!numX || !numY) return;
const handleMouseMove = (e: MouseEvent) => {
const { innerWidth, innerHeight } = window;
numX.worth = (e.clientX / innerWidth) * 100;
numY.worth = 100 - (e.clientY / innerHeight) * 100;
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, [numX, numY]);
What’s taking place right here:
- We use
clientX
andclientY
to trace the mouse place throughout the browser window. - The values are normalized to a 0–100 vary, matching what the animation expects.
- These normalized values are then handed to the Axis_X and Axis_Y inputs within the Rive State Machine, driving the interactive animation.
⚠️ Necessary: All the time bear in mind to take away the occasion listener when the element unmounts to keep away from reminiscence leaks and undesirable habits.
Step 6: Cleanup and Render the Element
useEffect(() => {
return () => rive?.cleanup();
}, [rive]);
And the render:
return (
);
cleanup()
— frees up assets when the element unmounts. All the time name this to forestall reminiscence leaks.setCanvasRef
andsetContainerRef
— these have to be linked to the right DOM parts to ensure that Rive to render the animation correctly.
And right here’s the entire code:
import {
useRive,
useStateMachineInput,
Format,
Match,
Alignment,
} from '@rive-app/react-canvas';
import { useEffect, useState } from 'react';
export operate RiveBackground({ className }: { className?: string }) {
const [isLoaded, setIsLoaded] = useState(false);
const { rive, setCanvasRef, setContainerRef } = useRive({
src: 'https://cdn.rive.app/animations/hero.riv',
animations: ['State Machine 1','Timeline 1','Timeline 2'
],
autoplay: true,
format: new Format({ match: Match.Cowl, alignment: Alignment.Heart }),
onLoad: () => setIsLoaded(true),
enableRiveAssetCDN: true,
});
const numX = useStateMachineInput(rive, 'State Machine 1', 'Axis_X', 0);
const numY = useStateMachineInput(rive, 'State Machine 1', 'Axis_Y', 0);
useEffect(() => {
if (!numX || !numY) return;
const handleMouseMove = (e: MouseEvent) => {
if (!numX || !numY) {
return;
}
const { innerWidth, innerHeight } = window;
numX.worth = (e.clientX / innerWidth) * 100;
numY.worth = 100 - (e.clientY / innerHeight) * 100;
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, [numX, numY]);
useEffect(() => {
return () => {
rive?.cleanup();
};
}, [rive]);
return (
);
}
Step 7: Use the Element
Now you should utilize the RiveBackground
like some other element:
Step 8: Preload the WASM File
To keep away from loading the .wasm file at runtime—which might delay the preliminary render—you may preload it in App.tsx:
import riveWASMResource from '@rive-app/canvas/rive.wasm';
That is particularly helpful should you’re optimizing for first paint or general efficiency.
Easy Parallax: A New Method with Information Binding
Within the first a part of this text, we used a traditional method with a State Machine to create the parallax animation in Rive. We constructed 4 separate animations (high, backside, left, proper), managed them utilizing enter variables, and blended their states to create clean movement. This methodology made sense on the time, particularly earlier than Information Binding help was launched.
However now that Information Binding is on the market in Rive, reaching the identical impact is far less complicated—only a few steps. Information binding in Rive is a system that connects editor parts to dynamic information and code through view fashions, enabling reactive, runtime-driven updates and interactions between design and growth.
On this part, we’ll present easy methods to refactor the unique Rive file and code utilizing the brand new method.
Updating the Rive File
- Take away the previous setup:
- Go to the State Machine.
- Delete the enter variables: high, backside, left, proper.
- Take away the mixing states and their related animations.
- Group the parallax layers:
- Wrap all of the parallax layers into a brand new group—e.g., ParallaxGroup.
- Create binding parameters:
- Choose ParallaxGroup and add:
- pointerX (Quantity)
- pointerY (Quantity)
- Choose ParallaxGroup and add:
- Bind coordinates:
- Within the properties panel, set:
- X → pointerX
- Y → pointerY
- Within the properties panel, set:
Now the group will transfer dynamically based mostly on values handed from JavaScript.
The Up to date JS Code
Earlier than we dive into the up to date JavaScript, let’s shortly outline an necessary idea:
When utilizing Information Binding in Rive, viewModelInstance
refers back to the runtime object that hyperlinks your Rive file’s bindable properties (like pointerX
or pointerY
) to your app’s logic. Within the Rive editor, you assign these properties to parts like positions, scales, or rotations. At runtime, your code accesses and updates them by the viewModelInstance
—permitting for real-time, declarative management with no need a State Machine.
With that in thoughts, right here’s how the brand new setup replaces the previous input-driven logic:
import { useRive } from '@rive-app/react-canvas';
import { useEffect, useState } from 'react';
export operate ParallaxEffect({ className }: { className?: string }) {
const [isLoaded, setIsLoaded] = useState(false);
const { rive, setCanvasRef, setContainerRef } = useRive({
src: 'https://cdn.rive.app/animations/hero.riv',
autoplay: true,
autoBind: true,
onLoad: () => setIsLoaded(true),
});
useEffect(() => {
if (!rive) return;
const vmi = rive.viewModelInstance;
const pointerX = vmi?.quantity('pointerX');
const pointerY = vmi?.quantity('pointerY');
if (!pointerX || !pointerY) return;
const handleMouseMove = (e: MouseEvent) => {
const { innerWidth, innerHeight } = window;
const x = (e.clientX / innerWidth) * 100;
const y = 100 - (e.clientY / innerHeight) * 100;
pointerX.worth = x;
pointerY.worth = y;
};
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
rive.cleanup();
};
}, [rive]);
return (
);
}
The Outcome
You get the identical parallax impact, however:
- with out enter variables or mixing;
- and not using a State Machine;
- with easy management through the ViewModel.
Official Stay Instance from Rive
👉 CodeSandbox: Information Binding Parallax
Conclusion
Information Binding is a serious step ahead for interactive Rive animations. Results like parallax can now be arrange sooner, extra reliably, and with cleaner logic. We strongly suggest this method for brand new initiatives.
Remaining Ideas
So why did we select Rive over Lottie for this undertaking?
- Interactivity: With Lottie, reaching the identical stage of interactivity would’ve required constructing a customized logic layer from scratch. With Rive, we obtained that habits baked into the file—plug and play.
- Optimization: Rive offers you extra management over every asset contained in the .riv file, and the output tends to be lighter general.
Our largest takeaway? Don’t be afraid to experiment with new instruments—particularly once they really feel like the correct match in your undertaking’s idea. Rive matched the playful, interactive vibe of Valley Adventures completely, and we’re excited to maintain exploring what it will possibly do.