Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 93 additions & 3 deletions runtimes/react/data-binding.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,41 @@ const boundInstance = rive?.viewModelInstance;

<Binding />

For React, no additional steps are needed to bind the view model instance to the Rive component. Passing the `rive` object to `useViewModelInstance` handles this automatically.
For React, no additional steps are needed to bind the view model instance to the Rive component. Passing the `rive` object to `useViewModelInstance` handles this automatically — the hook sets the instance and schedules a single `bind()`.

<Tip>
**Set every instance first, then call `bind()` once.** Setting an instance is cheap; `bind()` carries a much higher cost, and calling it per instance pays that cost again each time. Batching matters most at initialization, and on graphics with many bindings or deeply nested artboards.

The view model hooks already do this for you: hooks that resolve in the same render commit share one coalesced `bind()`. When you bind by hand — in `onRiveReady`, for instance — stage each instance with `setViewModelInstance()` / `setGlobalViewModelInstance()` and flush them with a single `bind()`.
</Tip>

### Binding before the first frame

The view model hooks run after Rive has loaded, which means the first frame has already advanced and rendered before they bind, even with `autoplay: false`. If you want to set data before the first render, do the data binding setup in the `onRiveReady` callback of `useRive` instead. It receives the `Rive` instance synchronously as soon as the file loads, and before the state machine advances, so you can stage instances and set property values that the very first frame renders with.

```tsx
import { useRive, ViewModel, ViewModelInstance } from '@rive-app/react-webgl2';

const { RiveComponent } = useRive({
src: 'your_file.riv',
stateMachines: 'MyStateMachine',
autoplay: true,
autoBind: false, // Set up the instances yourself before the first frame
onRiveReady: (rive) => {
const mainVM = rive.viewModelByName('MyViewModel') as ViewModel;
const instance = mainVM.defaultInstance() as ViewModelInstance;

instance.string('username').value = 'Rive';

rive.setViewModelInstance(instance);
rive.bind(); // Flush the staged instances
},
});
```

<Tip>
Use `onRiveReady` rather than `onLoad` for any pre-render data binding work. Anything you can reach from the `Rive` instance, including [global view models](#global-view-models), can be prepared there.
</Tip>

<AutoBinding />

Expand All @@ -129,20 +163,76 @@ const { rive, RiveComponent } = useRive({
// ... other options
});

// Once loaded, the instance is available:
// Once loaded, the main view model instance is available:
const boundInstance = rive?.viewModelInstance;
```

### Global View Models

A view model marked **global** in the editor is shared across the whole file rather than owned by a single artboard. The file holds one slot per global view model, addressed by name, so every artboard and state machine reads from the same instance — useful for app-wide state such as a theme, session, or settings.

Use the `useGlobalViewModelInstance` hook to resolve an instance and register it under a global's name. It takes the view model (from `useViewModel`), the name of the global slot, and the same kind of selector parameters as `useViewModelInstance`:

```tsx
import {
useRive,
useViewModel,
useGlobalViewModelInstance,
useViewModelInstanceColor,
} from '@rive-app/react-webgl2';

const { rive, RiveComponent } = useRive({
src: 'your_file.riv',
stateMachines: 'State Machine 1',
autoBind: false,
});

const themeViewModel = useViewModel(rive, { name: 'Theme' });

// Resolve the default instance and register it as the "Theme" global
const themeInstance = useGlobalViewModelInstance(themeViewModel, 'Theme', { rive });

// Property hooks work exactly as they do for the main instance
const { value: bgColor, setValue: setBgColor } = useViewModelInstanceColor(
'backgroundColor',
themeInstance
);
```

The third argument selects which instance to resolve:

- `{ rive }` — the view model's default instance (the implicit default)
- `{ instanceName: 'Dark', rive }` — the instance with that name
- `{ useNew: true, rive }` — a new blank instance
- `{ instance: myInstance, rive }` — an instance you created yourself

Passing `rive` is what registers the instance as the global and schedules the bind. Omit it to resolve an instance without binding it.

<Note>
You only need a hook for the globals whose properties you want to read or write. Binding fills every slot you did not set — including the main view model instance — with that view model's default instance from the file.
</Note>

To list a file's globals or read back whichever instance currently occupies a slot, go through the `rive` instance directly:

```tsx
const names = rive?.globalViewModelNames(); // e.g. ["Theme", "Session"]
const themeInstance = rive?.globalViewModelInstance('Theme'); // ViewModelInstance | null
```

`globalViewModelInstance()` never creates an instance — it returns `null` until one has been set by a hook, set in `onRiveReady`, or by `autoBind: true`.

<Properties />

<ListingProperties />

```typescript
// Access properties from the view model returned by useViewModel
const viewModel = useViewModel(rive);
console.log(viewModel?.properties);
console.log(viewModel?.properties); // [{ name: "status", type: "enum", enumName: "Status" }, ...]
```

Each entry has a `name` and a `type`. Properties of type `"enum"` also carry an `enumName`, which you can match against the results of [`rive.enums()`](#enums) to look up that enum's available values.

<ReadingAndWritingProperties />

Use the specific hook for a given property type to get and set property values.
Expand Down
2 changes: 1 addition & 1 deletion runtimes/react/parameters-and-return-values.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Most of these parameters come from the underlying web runtime configuration item

The following parameters are specific to `useRive`:

- `onRiveReady` - *(optional)* Callback invoked once the Rive file has fully loaded and the `Rive` instance is initialized. The callback receives the `Rive` instance as its argument: `(rive: Rive) => void`. Use this to set data binding properties or interact with the `Rive` object before the state machine starts.
- `onRiveReady` - *(optional)* Callback invoked once the Rive file has fully loaded and the `Rive` instance is initialized. The callback receives the `Rive` instance as its argument: `(rive: Rive) => void`. It fires synchronously before the state machine advances, so this is where **all pre-render data binding work belongs** — staging view model instances with `setViewModelInstance()` / `setGlobalViewModelInstance()`, setting property values, and flushing them with `bind()`. The data binding hooks resolve after the first frame has already rendered. See [Binding before the first frame](/runtimes/react/data-binding#binding-before-the-first-frame).

<Tip>
Use `onRiveReady` as opposed to passing in an `onLoad` callback. `onRiveReady` will be invoked when JS emits the `RiveEvent.Load` event.
Expand Down
4 changes: 4 additions & 0 deletions runtimes/react/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ There are three main ways to use Rive in a React app:
<Note>
The Rive canvas sizes itself based on its container. If nothing appears, make sure the parent element has a defined width and height.
</Note>

<Tip>
The property hooks run after Rive has loaded, so the first frame renders with whatever data the file shipped with, even with `autoplay: false`. To set values *before* anything renders, do that work with the `rive` instance passed to the `onRiveReady` callback of `useRive`. See [Binding before the first frame](/runtimes/react/data-binding#binding-before-the-first-frame).
</Tip>
</Tab>
<Tab title="Rive Component">
Rive React provides a basic component as its default import for displaying simple animations with a few props you can set, such as `artboard` and `layout`.
Expand Down
86 changes: 86 additions & 0 deletions runtimes/web/data-binding.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,24 @@ const r = new rive.Rive({
});
```

`bindViewModelInstance()` is a convenience method that sets a view model instance on the state machine and applies it in one step. To apply several instances at once — for example the main instance plus one or more [global view models](#global-view-models) — we recommend staging each view model with a `set` method, then flush them with a single `bind()`:

```typescript
r.setViewModelInstance(vmi); // staged, not applied yet
r.setGlobalViewModelInstance("Theme", themeVmi); // staged, not applied yet
r.bind(); // applies everything in one rebind
```

`bindViewModelInstance(vmi)` is shorthand for setting the main instance with `setViewModelInstance(vmi)` followed by `bind()`.

<Tip>
**Set every instance first, then call `bind()` once.** The `set` methods are cheap; `bind()` carries a much higher cost, and calling it per view model instance pays that cost again each time. Batching matters most at initialization, and on graphics with many bindings or deeply nested artboards.
</Tip>

<Note>
When a state machine is running, `bind()` also fills in any view models that were not set by using their default instances — the artboard's default view model instance, plus a default instance for each global view model in the file.
</Note>

<AutoBinding />

```typescript {4}
Expand All @@ -114,6 +132,51 @@ const r = new rive.Rive({
});
```

### Global View Models

A view model marked **global** in the editor is shared across the whole file rather than owned by a single artboard. The file holds one slot per global view model, addressed by name, so every artboard and state machine reads from the same instance — useful for app-wide state such as a theme, session, or settings.

To bind the global view models manually (as opposed to using `autoBind: true`):

```typescript
const names = r.globalViewModelNames(); // e.g. ["Theme", "Breakpoints"]
```

This is also available on `RiveFile`, so you can inspect a file's globals before creating a `Rive` instance.

Assign an instance to a global slot with `setGlobalViewModelInstance()`, then apply it with `bind()`. It returns `false` if the name does not match a global view model in the file:

```typescript
const r = new rive.Rive({
src: "my_rive_file.riv",
canvas: document.getElementById("canvas"),
stateMachines: "State Machine 1",
autoBind: false,
onLoad: () => {
const themeVM = r.viewModelByName("Theme");
const themeVMI = themeVM.instanceByName("Dark");

// Read and write properties exactly as you would on the global view model instance
themeVMI.color("background").value = parseInt('ff2266dd', 16);

r.setGlobalViewModelInstance("Theme", themeVMI); // Staged, not applied yet
r.bind(); // This will also create a default instance for the main view model implicitly
}
});
```

Read back whichever instance currently occupies a slot with `globalViewModelInstance()`:

```typescript
const themeVMI = r.globalViewModelInstance("Theme"); // ViewModelInstance | null
```

Global instances are never created implicitly by the getter. `globalViewModelInstance()` returns `null` until an instance has been set, or created for you by `bind()` / `autoBind: true`.

<Note>
With `autoBind: true`, Rive creates a default instance for every global view model in the file alongside the artboard's default instance, so `globalViewModelInstance(name)` is populated by the time `onLoad` fires.
</Note>

### Get View Model from Instance

When working with view model instances, you may want to get a reference to the view model the instance came from so you can dynamically create more instances of the same type (i.e. creating instances from lists).
Expand Down Expand Up @@ -146,6 +209,8 @@ for (let i = 0; i < 10; i++) {
console.log(properties);
```

Each entry has a `name` and a `type`. Properties of type `"enum"` also carry an `enumName`, which you can match against the results of [`r.enums()`](#enums) to look up that enum's available values.

<ReadingAndWritingProperties />

```typescript
Expand Down Expand Up @@ -282,6 +347,27 @@ const r = new rive.Rive({
});
```

### Fonts

Font properties let you swap the typeface used by bound text at runtime. Decode font bytes with `rive.decodeFont()` and assign the result to the property's `value`.

```typescript
const r = new rive.Rive({
autoBind: true,
onLoad: async () => {
// Access the current instance that was auto-bound
let vmi = r.viewModelInstance;

// Get the font property by name
const fontProperty = vmi.font("bound_font");

const res = await fetch("/fonts/Inter.ttf");
const font = await rive.decodeFont(new Uint8Array(await res.arrayBuffer()));
fontProperty.value = font;
}
});
```

<Lists />

<Demos examples={['dataBindingLists']} runtime="web" />
Expand Down
15 changes: 14 additions & 1 deletion runtimes/web/rive-parameters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ View model APIs on `Rive` are documented in depth on the [data binding](/runtime
- `.enum(path: string): ViewModelInstanceEnum`
- `.list(path: string): ViewModelInstanceList`
- `.image(path: string): ViewModelInstanceAssetImage`
- `.font(path: string): ViewModelInstanceAssetFont`
- `.artboard(path: string): ViewModelInstanceArtboard`
- `.viewModel(path: string): ViewModelInstance`
- `.viewModelName` - Getter property to return the name of the `ViewModel` the instance is created from
Expand All @@ -128,7 +129,12 @@ View model APIs on `Rive` are documented in depth on the [data binding](/runtime
- `viewModelByIndex(index: number): ViewModel | null` - returns a `ViewModel` specified by index from the `.riv` file, or null if it doesn't exist or the file isn't loaded yet
- `viewModelByName(name: string): ViewModel | null` - returns a `ViewModel` specified by name, or null if it doesn't exist or the file isn't loaded yet
- `defaultViewModel(): ViewModel | null` - returns the default `ViewModel` for the file, or null if it doesn't exist or the file isn't loaded yet
- `bindViewModelInstance(instance: ViewModelInstance | null): void` - Binds a `ViewModelInstance` to the state machine. Note that this is taken care of automatically if `autoBind` is `true`.
- `bindViewModelInstance(instance: ViewModelInstance | null): void` - Sets and binds a main `ViewModelInstance` to the state machine. Note that this is taken care of automatically if `autoBind` is `true`.
- `setViewModelInstance(instance: ViewModelInstance | null): void` - Sets a main `ViewModelInstance` to the state machine, but does not bind it. Call `bind()` to flush this change.
- `setGlobalViewModelInstance(name: string, instance: ViewModelInstance): boolean` - Sets (or replaces) the `ViewModelInstance` occupying the global view model slot named `name`, but does not bind it. Call `bind()` to flush this change. Returns `false` if `name` does not match a global view model in the file.
- `globalViewModelInstance(name: string): ViewModelInstance | null` - Returns the `ViewModelInstance` currently occupying the named global view model slot, or `null` if none has been set or created yet. This is a pure read — it never creates an instance.
- `globalViewModelNames(): string[]` - Names of the file's global view models, in file order. Use these with `setGlobalViewModelInstance()` / `globalViewModelInstance()`.
- `bind(): void` - Flushes any set main `ViewModelInstance` and global `ViewModelInstance`s to the state machine. When a state machine is running, it also creates default view model instances for the main and global view models on the file if they were not set yet. This is taken care of automatically if `autoBind` is `true`.
- `enums(): DataEnum[]` - List of enums defined in the file
- `getBindableArtboard(name: string): BindableArtboard | null` - Returns a named artboard as a `BindableArtboard`, which exposes data-binding APIs. Once you have a bindable artboard, you can set the `.viewModel` property on that artboard to bind a `ViewModelInstance`. Bindable artboards can be set as a value to an artboard property on a different ViewModel. Returns `null` if the artboard doesn't exist or the file isn't loaded.
- `getDefaultBindableArtboard(): BindableArtboard | null` - Returns the file's default artboard as a `BindableArtboard`, or `null` if not available.
Expand Down Expand Up @@ -303,6 +309,7 @@ interface RiveLoadParameters {
stateMachines?: string | string[];
useOffscreenRenderer?: boolean;
shouldDisableRiveListeners?: boolean;
tabIndex?: number;
}

load(params: RiveLoadParameters): void
Expand Down Expand Up @@ -566,6 +573,12 @@ Returns the default artboard as a `BindableArtboard`, or `null` if not available

Returns a `ViewModel` from the file by name, or `null` if it doesn’t exist or the file isn’t loaded yet. Equivalent to the `Rive` class's `.viewModelByName()` API, but accessible directly on the file handle before a `Rive` instance is created.

#### `globalViewModelNames()`

`globalViewModelNames(): string[]`

Names of the file's [global view models](/runtimes/web/data-binding#global-view-models), in file order. Equivalent to the `Rive` class's `.globalViewModelNames()` API, but accessible directly on the file handle before a `Rive` instance is created.

#### `cleanup()`

`cleanup(): void`
Expand Down