From 25c2459cd755492d4acf09327884d0315979a697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 13 Jul 2026 08:31:49 +0200 Subject: [PATCH 1/8] docs(react-native): async view model instance creation via useViewModelInstance({ async: true }) --- runtimes/react-native/data-binding.mdx | 66 ++++++++++++++--------- runtimes/react-native/migration-guide.mdx | 59 ++++++++++++++++++-- runtimes/react-native/react-native.mdx | 37 +++++++------ 3 files changed, 118 insertions(+), 44 deletions(-) diff --git a/runtimes/react-native/data-binding.mdx b/runtimes/react-native/data-binding.mdx index 6314fd66..9e854387 100644 --- a/runtimes/react-native/data-binding.mdx +++ b/runtimes/react-native/data-binding.mdx @@ -58,49 +58,63 @@ import { Demos } from "/snippets/demos.jsx"; Use the `useViewModelInstance` hook to create a view model instance. You can pass a `RiveFile`, `ViewModel`, or `RiveViewRef` as the source. + Pass `async: true` to create the instance via the async runtime APIs, off the JS thread. The hook returns `{ instance, isLoading, error }` — the instance is not available on the first render, so gate rendering on it: + ```tsx + import { Text } from 'react-native'; import { useRiveFile, useViewModelInstance, RiveView } from '@rive-app/react-native'; - const { riveFile } = useRiveFile(require('./my_file.riv')); + const { riveFile, error: fileError } = useRiveFile(require('./my_file.riv')); // From RiveFile — default artboard's ViewModel, default instance - const { instance } = useViewModelInstance(riveFile); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); + + if (fileError || error) return {(fileError ?? error)!.message}; + + return riveFile && instance && ( + + ); + ``` + + All lookup options combine with `async: true`: + ```tsx // Specify artboard or ViewModel name (mutually exclusive) - const { instance } = useViewModelInstance(riveFile, { artboardName: 'MainArtboard' }); - const { instance } = useViewModelInstance(riveFile, { viewModelName: 'Settings' }); + const { instance } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); + const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance - const { instance } = useViewModelInstance(riveFile, { instanceName: 'PersonInstance' }); - const { instance } = useViewModelInstance(riveFile, { viewModelName: 'Settings', instanceName: 'UserSettings' }); + const { instance } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); + const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); // From a ViewModel object - const viewModel = riveFile?.viewModelByName('My View Model'); - const { instance: namedInstance } = useViewModelInstance(viewModel, { name: 'My Instance' }); - const { instance: newInstance } = useViewModelInstance(viewModel, { useNew: true }); + const viewModel = await riveFile?.viewModelByNameAsync('My View Model'); + const { instance: namedInstance } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); + const { instance: newInstance } = useViewModelInstance(viewModel, { async: true, useNew: true }); - // With required: true (throws if null, use with Error Boundary) - const { instance } = useViewModelInstance(riveFile, { required: true }); + // With required: true (throws once resolved to null, use with Error Boundary) + const { instance } = useViewModelInstance(riveFile, { async: true, required: true }); - // With onInit to set initial values synchronously + // With onInit to set initial values before the instance is exposed or bound const { instance } = useViewModelInstance(riveFile, { + async: true, onInit: (vmi) => { vmi.numberProperty('health')?.set(100); }, }); - - return ( - - ); ``` - You can also get the auto-bound instance from a `RiveViewRef`: + + Without `async: true` the instance is created synchronously during render, via deprecated runtime APIs that block the JS thread. Those overloads are deprecated — `async: true` becomes the default in the next major release. + + + You can also get the auto-bound instance from a `RiveViewRef` — the hook waits for the view's auto-bound instance to become available: ```tsx import { useRive, useViewModelInstance } from '@rive-app/react-native'; const { riveViewRef, setHybridRef } = useRive(); - const { instance } = useViewModelInstance(riveViewRef); + const { instance } = useViewModelInstance(riveViewRef, { async: true }); ``` @@ -179,7 +193,7 @@ import { Demos } from "/snippets/demos.jsx"; import { RiveView, useRiveFile, useViewModelInstance } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile); + const { instance } = useViewModelInstance(riveFile, { async: true }); return ( (undefined); const handleLoadImage = async () => { @@ -665,7 +679,7 @@ import { Demos } from "/snippets/demos.jsx"; } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile); + const { instance } = useViewModelInstance(riveFile, { async: true }); // Get the list property with manipulation functions const { @@ -786,7 +800,7 @@ import { Demos } from "/snippets/demos.jsx"; } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile); + const { instance } = useViewModelInstance(riveFile, { async: true }); const { value: category, setValue: setCategory, error } = useRiveEnum( 'category', diff --git a/runtimes/react-native/migration-guide.mdx b/runtimes/react-native/migration-guide.mdx index 0660774a..b86d247a 100644 --- a/runtimes/react-native/migration-guide.mdx +++ b/runtimes/react-native/migration-guide.mdx @@ -3,6 +3,59 @@ title: "Migration Guide" description: "Learn how to migrate your React Native app when upgrading between major versions of the Rive React Native runtime, including breaking changes and new features." --- +## Migrating to `v0.5.0`+ + +This release introduces async view model instance creation and deprecates the synchronous creation path. + +### `useViewModelInstance` Gains `async: true` and `isLoading` + +Pass `async: true` to create the instance via the async runtime APIs, off the JS thread. The result now includes `isLoading`, and the instance is not available on the first render — gate rendering on it. Without `async: true` the instance is still created synchronously during render, but those overloads are now deprecated (`async: true` becomes the default in the next major release). + + + + ```tsx + const { riveFile, error: fileError } = useRiveFile(require('./animation.riv')); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); + + if (fileError || error) return {(fileError ?? error)!.message}; + if (isLoading || !instance) return ; + return ; + ``` + + + + ```tsx + const { riveFile } = useRiveFile(require('./animation.riv')); + const { instance, error } = useViewModelInstance(riveFile); + + if (error) return {error.message}; + if (!instance) return ; + return ; + ``` + + + + +`async` must stay constant for the lifetime of the component — remount (e.g. change `key`) to switch modes. + +### `useRive` Ref Starts `undefined` + +`useRive().riveViewRef` starts as `undefined` (view pending) instead of `null` (failed/detached), mirroring the `useRiveFile` convention. Code using `riveViewRef === null` as a "not ready yet" check should use `riveViewRef == null` or optional chaining instead. + +### Other Changes + +- On the synchronous (deprecated) path, a `null` source now settles to a terminal `{ instance: null, isLoading: false }` instead of reporting a loading state indefinitely. +- On Android, reading the auto-bound instance from a view ref right after mount can briefly resolve `null` while binding completes. The `async: true` path waits for the instance; one-shot reads via the deprecated sync path should be avoided. + +### Quick Reference + +| Previous | Replacement | +|---|---| +| `const { instance, error } = useViewModelInstance(file)` | `const { instance, isLoading, error } = useViewModelInstance(file, { async: true })` | +| `riveViewRef === null` (not-ready check) | `riveViewRef == null` | + +--- + ## Migrating to `v0.4.0`+ This release improves error transparency and loading semantics for hooks, preparing for the async experimental runtime. @@ -231,7 +284,7 @@ Synchronous `useMemo` chains for ViewModel setup should be replaced with `useSta ```tsx const { riveFile } = useRiveFile(require('./animation.riv')); - const { instance } = useViewModelInstance(riveFile); + const { instance } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber( 'health', @@ -346,7 +399,7 @@ const [setRiveRef, riveRef] = useRive(); const [health, setHealth] = useRiveNumber(riveRef, "health"); // New: hooks take viewModelInstance, return objects -const { instance: viewModelInstance } = useViewModelInstance(riveFile); +const { instance: viewModelInstance } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber( "health", viewModelInstance @@ -554,7 +607,7 @@ Main API changes: ```tsx const { riveFile } = useRiveFile(require('./animation.riv')); - const { instance: viewModelInstance } = useViewModelInstance(riveFile); + const { instance: viewModelInstance } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber('health', viewModelInstance); const { value: name, setValue: setName } = useRiveString('Player/Name', viewModelInstance); diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index d9a2ceaf..8035e935 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -218,13 +218,14 @@ This guide documents how to get started using the Rive React Native runtime. The This approach lets you set initial property values in the `onInit` callback before the view loads and decouples the `ViewModelInstance` from the `RiveView`. - ```ts Manually create view model instance focus={6-8, 12, 18} + ```ts Manually create view model instance focus={6-9, 13, 19} export default function QuickStart() { const { riveFile } = useRiveFile( require('path/to/quick_start.riv') ); const { riveViewRef, setHybridRef } = useRive(); const { instance: viewModelInstance } = useViewModelInstance(riveFile, { + async: true, onInit: (vmi) => vmi.numberProperty('health')!.set(20), }); @@ -246,13 +247,14 @@ This guide documents how to get started using the Rive React Native runtime. The Use the view model property hooks to update and listen to property changes. - ```ts Property hooks focus={10-37, 50-52} expandable + ```ts Property hooks focus={11-38, 51-53} expandable export default function QuickStart() { const { riveFile } = useRiveFile( require('path/to/quick_start.riv') ); const { riveViewRef, setHybridRef } = useRive(); const { instance: viewModelInstance } = useViewModelInstance(riveFile, { + async: true, onInit: (vmi) => vmi.numberProperty('health')!.set(20), }); @@ -372,35 +374,40 @@ This guide documents how to get started using the Rive React Native runtime. The ### `useViewModelInstance` - Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`: + Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`. Pass `async: true` to create the instance via the async runtime APIs, off the JS thread — the hook returns `{ instance, isLoading, error }` and the instance is not available on the first render: ```ts // From RiveFile — default artboard's ViewModel, default instance - const { instance } = useViewModelInstance(riveFile); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); // From RiveFile — specify artboard or ViewModel name (mutually exclusive) - const { instance } = useViewModelInstance(riveFile, { artboardName: 'MainArtboard' }); - const { instance } = useViewModelInstance(riveFile, { viewModelName: 'Settings' }); + const { instance } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); + const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance - const { instance } = useViewModelInstance(riveFile, { instanceName: 'PersonInstance' }); - const { instance } = useViewModelInstance(riveFile, { viewModelName: 'Settings', instanceName: 'UserSettings' }); + const { instance } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); + const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); // From a ViewModel object - const { instance: namedInstance } = useViewModelInstance(viewModel, { name: 'My Instance' }); - const { instance: newInstance } = useViewModelInstance(viewModel, { useNew: true }); + const { instance: namedInstance } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); + const { instance: newInstance } = useViewModelInstance(viewModel, { async: true, useNew: true }); - // With required: true (throws if null, use with Error Boundary) - const { instance } = useViewModelInstance(riveFile, { required: true }); + // With required: true (throws once resolved to null, use with Error Boundary) + const { instance } = useViewModelInstance(riveFile, { async: true, required: true }); - // With onInit to set initial values synchronously + // With onInit to set initial values before the instance is exposed or bound const { instance } = useViewModelInstance(riveFile, { + async: true, onInit: (vmi) => { vmi.numberProperty('health')!.set(100); }, }); ``` + + Without `async: true` the instance is created synchronously during render, via deprecated runtime APIs that block the JS thread. Those overloads are deprecated — `async: true` becomes the default in the next major release. + + Pass the `dataBind` prop in `RiveView`. ```ts return ( @@ -411,13 +418,13 @@ This guide documents how to get started using the Rive React Native runtime. The ); ``` - You can also get the auto-bound instance from a `RiveViewRef`: + You can also get the auto-bound instance from a `RiveViewRef` — the hook waits for the view's auto-bound instance to become available: ```javascript import { useRive, useViewModelInstance } from '@rive-app/react-native'; const { riveViewRef, setHybridRef } = useRive(); - const { instance } = useViewModelInstance(riveViewRef); + const { instance } = useViewModelInstance(riveViewRef, { async: true }); ``` See the [runtime data binding documentation](/runtimes/react-native/data-binding) for more information. From f758c36f82f0f815df13218749420a34bde541c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 13 Jul 2026 12:35:38 +0200 Subject: [PATCH 2/8] docs(react-native): frame async: true as a transitional loading-state opt-in --- runtimes/react-native/data-binding.mdx | 6 +++--- runtimes/react-native/migration-guide.mdx | 9 ++++++++- runtimes/react-native/react-native.mdx | 6 +++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/runtimes/react-native/data-binding.mdx b/runtimes/react-native/data-binding.mdx index 9e854387..c63cfdcf 100644 --- a/runtimes/react-native/data-binding.mdx +++ b/runtimes/react-native/data-binding.mdx @@ -58,7 +58,7 @@ import { Demos } from "/snippets/demos.jsx"; Use the `useViewModelInstance` hook to create a view model instance. You can pass a `RiveFile`, `ViewModel`, or `RiveViewRef` as the source. - Pass `async: true` to create the instance via the async runtime APIs, off the JS thread. The hook returns `{ instance, isLoading, error }` — the instance is not available on the first render, so gate rendering on it: + Pass `async: true` to opt in to asynchronous instance creation. The flag signals that your component handles the loading state: the hook returns `{ instance, isLoading, error }` and the instance is not available on the first render, so gate rendering on it. This prepares your code for the new runtime implementation, where creating an instance is an asynchronous operation. ```tsx import { Text } from 'react-native'; @@ -80,7 +80,7 @@ import { Demos } from "/snippets/demos.jsx"; ```tsx // Specify artboard or ViewModel name (mutually exclusive) - const { instance } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance @@ -105,7 +105,7 @@ import { Demos } from "/snippets/demos.jsx"; ``` - Without `async: true` the instance is created synchronously during render, via deprecated runtime APIs that block the JS thread. Those overloads are deprecated — `async: true` becomes the default in the next major release. + `async: true` is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. You can also get the auto-bound instance from a `RiveViewRef` — the hook waits for the view's auto-bound instance to become available: diff --git a/runtimes/react-native/migration-guide.mdx b/runtimes/react-native/migration-guide.mdx index b86d247a..eb20309b 100644 --- a/runtimes/react-native/migration-guide.mdx +++ b/runtimes/react-native/migration-guide.mdx @@ -9,7 +9,14 @@ This release introduces async view model instance creation and deprecates the sy ### `useViewModelInstance` Gains `async: true` and `isLoading` -Pass `async: true` to create the instance via the async runtime APIs, off the JS thread. The result now includes `isLoading`, and the instance is not available on the first render — gate rendering on it. Without `async: true` the instance is still created synchronously during render, but those overloads are now deprecated (`async: true` becomes the default in the next major release). +Pass `async: true` to opt in to asynchronous instance creation. The flag signals that your component handles the loading state: the result now includes `isLoading`, and the instance is not available on the first render — gate rendering on it. This prepares your code for the new runtime implementation, where creating an instance is an asynchronous operation. + +`async: true` is a transitional flag; the synchronous creation path is being phased out in stages: + +1. **Now** — calls without `async: true` are deprecated in TypeScript (deprecation warnings in your editor and via lint rules). +2. **Next** — calling without `async: true` will additionally log a runtime deprecation warning. +3. **Later** — the synchronous path is removed and `async: true` becomes required. +4. **Finally** — the flag itself is removed; the hook is simply asynchronous. diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index 8035e935..b0a37766 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -374,14 +374,14 @@ This guide documents how to get started using the Rive React Native runtime. The ### `useViewModelInstance` - Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`. Pass `async: true` to create the instance via the async runtime APIs, off the JS thread — the hook returns `{ instance, isLoading, error }` and the instance is not available on the first render: + Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`. Pass `async: true` to opt in to asynchronous instance creation — the flag signals that your component handles the loading state: the hook returns `{ instance, isLoading, error }` and the instance is not available on the first render. This prepares your code for the new runtime implementation, where creating an instance is an asynchronous operation. ```ts // From RiveFile — default artboard's ViewModel, default instance const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); // From RiveFile — specify artboard or ViewModel name (mutually exclusive) - const { instance } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance @@ -405,7 +405,7 @@ This guide documents how to get started using the Rive React Native runtime. The ``` - Without `async: true` the instance is created synchronously during render, via deprecated runtime APIs that block the JS thread. Those overloads are deprecated — `async: true` becomes the default in the next major release. + `async: true` is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. Pass the `dataBind` prop in `RiveView`. From 133edd5a75d333695951ab98f2fe980086a71529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 13 Jul 2026 12:48:40 +0200 Subject: [PATCH 3/8] docs(react-native): destructure isLoading and error in every useViewModelInstance example --- runtimes/react-native/data-binding.mdx | 32 +++++++++++------------ runtimes/react-native/migration-guide.mdx | 6 ++--- runtimes/react-native/react-native.mdx | 20 +++++++------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/runtimes/react-native/data-binding.mdx b/runtimes/react-native/data-binding.mdx index c63cfdcf..80aa6bf4 100644 --- a/runtimes/react-native/data-binding.mdx +++ b/runtimes/react-native/data-binding.mdx @@ -81,22 +81,22 @@ import { Demos } from "/snippets/demos.jsx"; ```tsx // Specify artboard or ViewModel name (mutually exclusive) const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); - const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance - const { instance } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); - const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); // From a ViewModel object const viewModel = await riveFile?.viewModelByNameAsync('My View Model'); - const { instance: namedInstance } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); - const { instance: newInstance } = useViewModelInstance(viewModel, { async: true, useNew: true }); + const { instance: namedInstance, isLoading, error } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); + const { instance: newInstance, isLoading, error } = useViewModelInstance(viewModel, { async: true, useNew: true }); // With required: true (throws once resolved to null, use with Error Boundary) - const { instance } = useViewModelInstance(riveFile, { async: true, required: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, required: true }); // With onInit to set initial values before the instance is exposed or bound - const { instance } = useViewModelInstance(riveFile, { + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, onInit: (vmi) => { vmi.numberProperty('health')?.set(100); @@ -114,7 +114,7 @@ import { Demos } from "/snippets/demos.jsx"; import { useRive, useViewModelInstance } from '@rive-app/react-native'; const { riveViewRef, setHybridRef } = useRive(); - const { instance } = useViewModelInstance(riveViewRef, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveViewRef, { async: true }); ``` @@ -193,7 +193,7 @@ import { Demos } from "/snippets/demos.jsx"; import { RiveView, useRiveFile, useViewModelInstance } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); return ( (undefined); const handleLoadImage = async () => { @@ -679,7 +679,7 @@ import { Demos } from "/snippets/demos.jsx"; } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); // Get the list property with manipulation functions const { @@ -800,7 +800,7 @@ import { Demos } from "/snippets/demos.jsx"; } from '@rive-app/react-native'; const { riveFile } = useRiveFile(require('./my_file.riv')); - const { instance } = useViewModelInstance(riveFile, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); const { value: category, setValue: setCategory, error } = useRiveEnum( 'category', diff --git a/runtimes/react-native/migration-guide.mdx b/runtimes/react-native/migration-guide.mdx index eb20309b..041a20fc 100644 --- a/runtimes/react-native/migration-guide.mdx +++ b/runtimes/react-native/migration-guide.mdx @@ -291,7 +291,7 @@ Synchronous `useMemo` chains for ViewModel setup should be replaced with `useSta ```tsx const { riveFile } = useRiveFile(require('./animation.riv')); - const { instance } = useViewModelInstance(riveFile, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber( 'health', @@ -406,7 +406,7 @@ const [setRiveRef, riveRef] = useRive(); const [health, setHealth] = useRiveNumber(riveRef, "health"); // New: hooks take viewModelInstance, return objects -const { instance: viewModelInstance } = useViewModelInstance(riveFile, { async: true }); +const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber( "health", viewModelInstance @@ -614,7 +614,7 @@ Main API changes: ```tsx const { riveFile } = useRiveFile(require('./animation.riv')); - const { instance: viewModelInstance } = useViewModelInstance(riveFile, { async: true }); + const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true }); const { value: health, setValue: setHealth } = useRiveNumber('health', viewModelInstance); const { value: name, setValue: setName } = useRiveString('Player/Name', viewModelInstance); diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index b0a37766..51ad5f53 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -224,7 +224,7 @@ This guide documents how to get started using the Rive React Native runtime. The require('path/to/quick_start.riv') ); const { riveViewRef, setHybridRef } = useRive(); - const { instance: viewModelInstance } = useViewModelInstance(riveFile, { + const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true, onInit: (vmi) => vmi.numberProperty('health')!.set(20), }); @@ -253,7 +253,7 @@ This guide documents how to get started using the Rive React Native runtime. The require('path/to/quick_start.riv') ); const { riveViewRef, setHybridRef } = useRive(); - const { instance: viewModelInstance } = useViewModelInstance(riveFile, { + const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true, onInit: (vmi) => vmi.numberProperty('health')!.set(20), }); @@ -382,21 +382,21 @@ This guide documents how to get started using the Rive React Native runtime. The // From RiveFile — specify artboard or ViewModel name (mutually exclusive) const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' }); - const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings' }); // instanceName can be combined with any of the above to pick a specific instance - const { instance } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); - const { instance } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, instanceName: 'PersonInstance' }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, viewModelName: 'Settings', instanceName: 'UserSettings' }); // From a ViewModel object - const { instance: namedInstance } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); - const { instance: newInstance } = useViewModelInstance(viewModel, { async: true, useNew: true }); + const { instance: namedInstance, isLoading, error } = useViewModelInstance(viewModel, { async: true, name: 'My Instance' }); + const { instance: newInstance, isLoading, error } = useViewModelInstance(viewModel, { async: true, useNew: true }); // With required: true (throws once resolved to null, use with Error Boundary) - const { instance } = useViewModelInstance(riveFile, { async: true, required: true }); + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, required: true }); // With onInit to set initial values before the instance is exposed or bound - const { instance } = useViewModelInstance(riveFile, { + const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, onInit: (vmi) => { vmi.numberProperty('health')!.set(100); @@ -424,7 +424,7 @@ This guide documents how to get started using the Rive React Native runtime. The import { useRive, useViewModelInstance } from '@rive-app/react-native'; const { riveViewRef, setHybridRef } = useRive(); - const { instance } = useViewModelInstance(riveViewRef, { async: true }); + const { instance, isLoading, error } = useViewModelInstance(riveViewRef, { async: true }); ``` See the [runtime data binding documentation](/runtimes/react-native/data-binding) for more information. From f7a8c52e72f061dfc71f8ce7860933e4ae3aef14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 17 Jul 2026 15:21:37 +0200 Subject: [PATCH 4/8] docs(react-native): async instance creation shipped in v0.4.18 --- runtimes/react-native/migration-guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/react-native/migration-guide.mdx b/runtimes/react-native/migration-guide.mdx index 041a20fc..bc5be17a 100644 --- a/runtimes/react-native/migration-guide.mdx +++ b/runtimes/react-native/migration-guide.mdx @@ -3,7 +3,7 @@ title: "Migration Guide" description: "Learn how to migrate your React Native app when upgrading between major versions of the Rive React Native runtime, including breaking changes and new features." --- -## Migrating to `v0.5.0`+ +## Migrating to `v0.4.18`+ This release introduces async view model instance creation and deprecates the synchronous creation path. From a6b99d1b5127aa5ab5ca32a6c7dac26e547bd9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 17 Jul 2026 15:22:25 +0200 Subject: [PATCH 5/8] docs(react-native): note async: true availability from v0.4.18 --- runtimes/react-native/data-binding.mdx | 2 +- runtimes/react-native/react-native.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtimes/react-native/data-binding.mdx b/runtimes/react-native/data-binding.mdx index 80aa6bf4..151e6661 100644 --- a/runtimes/react-native/data-binding.mdx +++ b/runtimes/react-native/data-binding.mdx @@ -105,7 +105,7 @@ import { Demos } from "/snippets/demos.jsx"; ``` - `async: true` is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. + `async: true` is available from `@rive-app/react-native@0.4.18`. It is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. You can also get the auto-bound instance from a `RiveViewRef` — the hook waits for the view's auto-bound instance to become available: diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index 51ad5f53..7cc4726a 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -405,7 +405,7 @@ This guide documents how to get started using the Rive React Native runtime. The ``` - `async: true` is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. + `async: true` is available from `@rive-app/react-native@0.4.18`. It is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. Pass the `dataBind` prop in `RiveView`. From 99caa30b109035573f366ce1490ded27da16383f Mon Sep 17 00:00:00 2001 From: Lance Snider Date: Mon, 20 Jul 2026 13:04:40 -0700 Subject: [PATCH 6/8] Update runtimes/react-native/data-binding.mdx --- runtimes/react-native/data-binding.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/react-native/data-binding.mdx b/runtimes/react-native/data-binding.mdx index 151e6661..75816324 100644 --- a/runtimes/react-native/data-binding.mdx +++ b/runtimes/react-native/data-binding.mdx @@ -105,7 +105,7 @@ import { Demos } from "/snippets/demos.jsx"; ``` - `async: true` is available from `@rive-app/react-native@0.4.18`. It is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. + `async: true` is available in `@rive-app/react-native@0.4.18` and later. The synchronous creation path is deprecated and will be removed in a future release. You can also get the auto-bound instance from a `RiveViewRef` — the hook waits for the view's auto-bound instance to become available: From 25cc777e1e641165a8cd94808e2409c89d07ad5e Mon Sep 17 00:00:00 2001 From: Lance Snider Date: Mon, 20 Jul 2026 13:04:49 -0700 Subject: [PATCH 7/8] Update runtimes/react-native/react-native.mdx --- runtimes/react-native/react-native.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index 7cc4726a..6087abeb 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -374,7 +374,9 @@ This guide documents how to get started using the Rive React Native runtime. The ### `useViewModelInstance` - Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`. Pass `async: true` to opt in to asynchronous instance creation — the flag signals that your component handles the loading state: the hook returns `{ instance, isLoading, error }` and the instance is not available on the first render. This prepares your code for the new runtime implementation, where creating an instance is an asynchronous operation. + Hook to create a view model instance from a `RiveFile`, `ViewModel`, or `RiveViewRef`. + + Pass `async: true` to opt in to asynchronous instance creation — the flag signals that your component handles the loading state. The hook returns `{ instance, isLoading, error }` and the instance is not available on the first render. This prepares your code for the new runtime implementation, where creating an instance is an asynchronous operation. ```ts // From RiveFile — default artboard's ViewModel, default instance From 4db322befd3adf9381bcdfce712bef4e06cf1db8 Mon Sep 17 00:00:00 2001 From: Lance Snider Date: Mon, 20 Jul 2026 13:04:58 -0700 Subject: [PATCH 8/8] Update runtimes/react-native/react-native.mdx --- runtimes/react-native/react-native.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtimes/react-native/react-native.mdx b/runtimes/react-native/react-native.mdx index 6087abeb..1d840c8a 100644 --- a/runtimes/react-native/react-native.mdx +++ b/runtimes/react-native/react-native.mdx @@ -407,7 +407,7 @@ This guide documents how to get started using the Rive React Native runtime. The ``` - `async: true` is available from `@rive-app/react-native@0.4.18`. It is a transitional flag: the synchronous creation path (which also blocks the JS thread during render) is being phased out in stages. Today, calls without `async: true` are deprecated in TypeScript; a future release adds a runtime deprecation warning; later, `async: true` becomes required; finally the flag is removed and the hook is always asynchronous. + `async: true` is available in `@rive-app/react-native@0.4.18` and later. The synchronous creation path is deprecated and will be removed in a future release. Pass the `dataBind` prop in `RiveView`.