Skip to main content

Migrating to the Async API (v0.3.2+)

This release introduces an async-first API to prepare for the new experimental Rive runtime. Synchronous methods that block the JS thread are deprecated and replaced with async equivalents. State machine input and text run methods are deprecated in favor of data binding and will be removed entirely in the experimental runtime (and therefore in upcoming @rive-app/react-native versions).

What’s Changed

  • Async methods replace all synchronous ViewModel and property accessors
  • Name-based access replaces count/index-based ViewModel and artboard lookups
  • getValueAsync() / set() replace property.value for reading and writing properties
  • useRiveNumber and similar hooks now start as undefined until the first listener emission
  • State machine inputs, text runs, and events are deprecated and will be removed in the experimental runtime — use data binding instead

Migration Steps

1. ViewModel Access

const names = await file.getViewModelNamesAsync();
const vm = await file.viewModelByNameAsync('Person');
const defaultVM = await file.defaultArtboardViewModelAsync();

2. Instance Creation

const instance = await vm.createDefaultInstanceAsync();
const named = await vm.createInstanceByNameAsync('player1');
const blank = await vm.createBlankInstanceAsync();
The useViewModelInstance hook handles ViewModel resolution and instance creation for you — in most cases you don’t need to call these methods directly.

3. Property Value Access

const num = await prop.getValueAsync();
prop.set(42);

4. Nested ViewModelInstance Access

const nested = await instance.viewModelAsync('Header');

5. List Property Access

const len = await listProp.getLengthAsync();
const item = await listProp.getInstanceAtAsync(0);

6. Artboard Access

const count = await file.getArtboardCountAsync();
const names = await file.getArtboardNamesAsync();

7. Hook Value Guarding

Property hooks (useRiveNumber, useRiveString, useRiveBoolean, useRiveColor, useRiveEnum) now return undefined as their initial value until the first listener emission.
const { value: count, setValue: setCount } = useRiveNumber(
  'Counter/Value',
  instance
);

// Guard for undefined in render
<Text>{count !== undefined ? count.toFixed(2) : '...'}</Text>

// Guard in updater functions
setCount((prev) => (prev ?? 0) + 1);

8. Async Setup Pattern

Synchronous useMemo chains for ViewModel setup should be replaced with useState + useEffect, or simplified with the useViewModelInstance hook.

Quick Reference

DeprecatedReplacement
file.viewModelByName(name)await file.viewModelByNameAsync(name)
file.defaultArtboardViewModel()await file.defaultArtboardViewModelAsync()
file.viewModelCount(await file.getViewModelNamesAsync()).length
file.viewModelByIndex(i)await file.viewModelByNameAsync(name)
file.artboardCount / artboardNamesawait file.getArtboardCountAsync() / getArtboardNamesAsync()
vm.createDefaultInstance()await vm.createDefaultInstanceAsync()
vm.createInstanceByName(name)await vm.createInstanceByNameAsync(name)
vm.createInstance()await vm.createBlankInstanceAsync()
instance.viewModel(path)await instance.viewModelAsync(path)
listProp.lengthawait listProp.getLengthAsync()
listProp.getInstanceAt(i)await listProp.getInstanceAtAsync(i)
prop.value (read)await prop.getValueAsync()
prop.value = x (write)prop.set(x)

Platform Caveats

LimitationDetails
replaceViewModel()No-op on Android. Works on iOS.
addListener() on image/list propertiesNo-op on both platforms. Poll with getLengthAsync() instead.
addInstanceAt() / swap() return valueAlways true on iOS. Android returns correct value.
instanceNameEmpty string except for instances created via createInstanceByNameAsync().
defaultArtboardViewModel() on AndroidUses a heuristic. Prefer viewModelByNameAsync(name) with an explicit name.

Migrating from v0.1.0 to v0.2.0

This change updates to a new major version of Nitro to resolve a view recycling issue. See the release for more details. The only change required is to update the version of Nitro used in your app.

Migrating from rive-react-native to @rive-app/react-native

The new Rive React Native runtime (@rive-app/react-native) is a complete rewrite built with Nitro Modules for improved performance and better React Native integration.
All your Rive graphics will still look and function the same as they did before.

What’s New

RiveFile Ownership: You now own the RiveFile object via the useRiveFile hook, enabling file caching, multiple instances from one file, and better resource management.
// Old: file managed internally
<Rive url="https://cdn.rive.app/animations/vehicles.riv" />

// New: you own the RiveFile
const { riveFile } = useRiveFile(require("./vehicles.riv"));
<RiveView file={riveFile} />
Enhanced Data Binding: Direct access to ViewModel and ViewModelInstance objects, enabling initialization hooks, multiple instances, and support for all property types (lists, images, artboards).
// Old: hooks take riveRef, return tuples
const [setRiveRef, riveRef] = useRive();
const [health, setHealth] = useRiveNumber(riveRef, "health");

// New: hooks take viewModelInstance, return objects
const viewModelInstance = useViewModelInstance(riveFile);
const { value: health, setValue: setHealth } = useRiveNumber(
  "health",
  viewModelInstance
);
Improved Error Handling: Error handling is improved. See the error handling documentation for more information.

Requirements

  • React Native: 0.78+ (0.79+ recommended)
  • Expo SDK: 53+ (for Expo users)
  • iOS: 15.1+
  • Android: SDK 24+
  • Xcode: 16.4+
  • JDK: 17+
  • Nitro Modules: 0.25.2+

Migration Steps

1. Installation

npm uninstall rive-react-native
npm install @rive-app/react-native react-native-nitro-modules
react-native-nitro-modules is required as this library relies on Nitro Modules.

2. Update Imports

// Old
import Rive from "rive-react-native";

// New
import { RiveView } from "@rive-app/react-native";

3. Loading Rive Files

const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
// Also supports: URL string, resource name, or ArrayBuffer

return <RiveView file={riveFile} style={{ width: 400, height: 400 }} />;
See loading Rive files for more information.

4. Component Migration

<RiveView
  file={riveFile}
  artboardName="MainArtboard"
  stateMachineName="State Machine 1"
  fit={Fit.Contain}
  autoPlay={true}
  style={{ width: 400, height: 400 }}
/>
See React Native runtime and props documentation for more information.

5. View Reference Migration

const { riveViewRef, setHybridRef } = useRive();

riveViewRef?.play();
riveViewRef?.pause();

<RiveView hybridRef={setHybridRef} file={riveFile} />
See Rive ref methods for more information.

6. State Machine Inputs (Deprecated)

These methods are deprecated. Migrate to data binding instead.
riveViewRef?.setNumberInputValue('level', 5);
riveViewRef?.setBooleanInputValue('isActive', true);
riveViewRef?.triggerInput('buttonPressed');
See state machine inputs for more information.

7. Rive Events (Deprecated)

These methods are deprecated. Use data binding triggers instead.
useEffect(() => {
  const handleEvent = (event: RiveEvent) => console.log(event);
  riveViewRef?.onEventListener(handleEvent);
  return () => riveViewRef?.removeEventListeners();
}, [riveViewRef]);
See runtime events for more information.

8. Data Binding

The new runtime significantly improves data binding by giving you direct access to ViewModelInstance objects. This enables:
  • Initialization hooks - Set initial property values before rendering with onInit callback
  • Multiple instances - Create and manage multiple view model instances from the same file
  • Advanced property types - Full support for lists, images, artboards, and nested view models
  • Better React integration - Property hooks integrate seamlessly with React state and lifecycle
Main API changes:
  • Property hooks take viewModelInstance instead of riveRef
  • Hooks return objects instead of tuples
  • Parameters are swapped: (path, viewModelInstance) vs (riveRef, path)
const { riveFile } = useRiveFile(require('./animation.riv'));
const viewModelInstance = useViewModelInstance(riveFile);

const { value: health, setValue: setHealth } = useRiveNumber('health', viewModelInstance);
const { value: name, setValue: setName } = useRiveString('Player/Name', viewModelInstance);
const { trigger } = useRiveTrigger('gameOver', viewModelInstance, {
  onTrigger: () => console.log('Game Over!')
});

<RiveView file={riveFile} dataBind={viewModelInstance} />
See react-native/data binding for more information.

9. Out of Band Assets

const { riveFile } = useRiveFile(
  require('./animation.riv'),
  {
    referencedAssets: {
      'Inter-594377': {
        source: require('./fonts/Inter-594377.ttf'),
      },
      'my-image': {
        source: { uri: 'https://example.com/image.png' },
      },
    },
  }
);
See loading assets for more information.

10. Text Run Updates (Deprecated)

Direct text run methods (.setTextRunValue(), .getTextRunValue()) are deprecated. Migrate to data binding strings instead.
See text runs for more information.

11. Callbacks

<RiveView
  file={riveFile}
  onError={(error) => console.error('Rive error:', error)}
/>

// For state changes, use data binding listeners. For example, a trigger:
const { trigger } = useRiveTrigger('onStateChange', viewModelInstance, {
  onTrigger: () => console.log('State changed')
});
See data binding for more information.

Getting Help

If you encounter issues:
  1. Check the new runtime documentation
  2. Review the Data Binding guide
  3. See the example app
  4. Visit the Rive community forums
  5. Report issues on GitHub