Overview

Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the Overview.

Data Binding Concepts

An overview of core data binding concepts.

View Models

View models describe a set of properties, but cannot themselves be used to get or set values - that is the role of view model instances.

To begin, we need to get a reference to a particular view model. This can be done either by index, by name, or the default for a given artboard, and is done from the Rive file. The default option refers to the view model assigned to an artboard by the dropdown in the editor.

Access a View Model from the created Rive object in the onLoad callback:

const rive = new rive.Rive({
    onLoad: () => {
        // The Rive object is now loaded and ready to use.
    }
});

Once Rive is loaded, you can access the view models using the following methods:

// Get reference by name
const namedVM = rive.viewModelByName("My View Model");

// Get reference by index
for (let i = 0; i < rive.viewModelCount; i++) {
    const indexedVM = rive.viewModelByIndex(i);
}

// Get reference to the default view model
const defaultVM = rive.defaultViewModel();

Alternatively, if you have access to the underlying Rive File object you can access the above methods on the file.

const namedVM = file.viewModelByName("My View Model");
const indexedVM = file.viewModelByIndex(0);
const defaultVM = file.defaultArtboardViewModel(artboard);

View Model Instances

Once we have a reference to a view model, it can be used to create an instance. When creating an instance, you have four options:

  1. Create a blank instance - Fill the properties of the created instance with default values as follows:

    TypeValue
    Number0
    StringEmpty string
    BooleanFalse
    Color0xFF000000
    TriggerUntriggered
    EnumThe first value
    Nested view modelNull
  2. Create the default instance - Use the instance labelled “Default” in the editor. Usually this is the one a designer intends as the primary one to be used at runtime.

  3. Create by index - Using the order returned when iterating over all available instances. Useful when creating multiple instances by iteration.

  4. Create by name - Use the editor’s instance name. Useful when creating a specific instance.

// Create a blank instance from a view model (ViewModel)
const vmiBlank = viewModel.instance();

// Create a default instance from a view model (ViewModel)
const vmiDefault = viewModel.defaultInstance();

// Create an instance by index from a view model (ViewModel)
for (let i = 0; i < viewModel.instanceCount; i++) {
    const vmiIndexed = viewModel.instanceByIndex(i);
}

// Create an instace by name from a view model (ViewModel)
const vmiNamed = viewModel.instanceByName("My Instance");

The created instance can then be assigned to a state machine or artboard. This establishes the bindings set up at edit time.

It is preferred to assign to a state machine, as this will automatically apply the instance to the artboard as well. Only assign to an artboard if you are not using a state machine, i.e. your file is static or uses linear animations.

The initial values of the instance are not applied to their bound elements until the state machine or artboard advances.
const rive = new rive.Rive({
    autoBind: false, // This should be set to false (default)
    onLoad: () => {
        const vm = rive.viewModelByName("My View Model");
        const vmi = vm.instanceByName("My Instance");

        // Manually bind by applying the instance to the state machine and artboard
        rive.bindViewModelInstance(vmi);
    }
});

Auto-Binding

Alternatively, you may prefer to use auto-binding. This will automatically bind the default view model of the artboard using the default instance to both the state machine and the artboard. The default view model is the one selected on the artboard in the editor dropdown. The default instance is the one marked “Default” in the editor.

const rive = new rive.Rive({
    src: "my_rive_file.riv",
    canvas: document.getElementById("canvas"),
    autoBind: true,
    onLoad: () => {
        // Access the current instance that was auto-bound
        let boundInstance = rive.viewModelInstance;
    }
});

Properties

A property is a value that can be read, set, or observed on a view model instance. Properties can be of the following types:

TypeSupported
Floating point numbers
Booleans
Triggers
Strings
Enumerations
Colors
Nesting
Lists🚧 Coming soon
Images🚧 Coming soon

Property descriptors can be inspected on a view model to discover at runtime which are available. These are not the mutable properties themselves though - once again those are on instances. These descriptors have a type and name.

// A list of properties on a view model (ViewModel)
const properties = viewModel.properties;
console.log(properties);

References to these properties can be retrieved by name or path.

Some properties are mutable and have getters, setters, and observer operations for their values. Getting or observing the value will retrieve the latest value set on that property’s binding, as of the last state machine or artboard advance. Setting the value will update the value and all of its bound elements.

After setting a property’s value, the changes will not apply to their bound elements until the state machine or artboard advances.
const rive = new rive.Rive({
    autoBind: true,
    onLoad: () => {
        // Access the current instance that was auto-bound
        let vmi = rive.viewModelInstance;

        // Booleans
        const booleanProperty = vmi.boolean("My Boolean Property");
        const booleanValue = booleanProperty.value;
        booleanProperty.value = true;

        // Strings
        const stringProperty = vmi.string("My String Property");
        const stringValue = stringProperty.value;
        stringProperty.value = "Hello, Rive!";

        // Numbers
        const numberProperty = vmi.number("My Number Property");
        const numberValue = numberProperty.value;
        numberProperty.value = 10;

        // Colors
        const colorProperty = vmi.color("My Color Property");
        const colorValue = colorProperty.value;
        colorProperty.value = 0xFF000000; // Set color to black with 100% opacity

        // Other ways to set color
        colorProperty.rgb(255, 0, 0); // Set RGB to red
        colorProperty.rbga(255, 0, 0, 128); // Set RGBA to red with 50% opacity
        colorProperty.argba(128, 255, 0, 0); // Set RGBA to red with 50% opacity
        colorProperty.opacity(0.5); // Set opacity to 50%

        // Triggers
        const triggerProperty = vmi.trigger("My Trigger Property");
        triggerProperty.trigger();

        // Enumerations
        const enumProperty = vmi.enum("My Enum Property");
        const enumValue = enumProperty.value;
        enumProperty.value = "Option1";
    }
});

Nested Property Paths

View models can have properties of type view model, allowing for arbitrary nesting. You can chain property calls on each instance starting from the root until you get to the property of interest. Alternatively, you can do this through a path parameter, which is similar to a URI in that it is a forward slash delimited list of property names ending in the name of the property of interest.

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

        const nestedNumberByChain = vmi
            .viewModel("My Nested View Model")
            .viewModel("My Second Nested VM")
            .number("My Nested Number");

        const nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number");
    }
});

Observability

You can observe changes over time to property values, either by using listeners or a platform equivalent method. Once observed, you will be notified when the property changes are applied by a state machine advance, whether that is a new value that has been explicitly set or if the value was updated as a result of a binding. Observing trigger properties is an alternative method to receive events from the editor, as compared to Rive Events.

Adding an observer to a property is done by calling the on method on the property.

public on(callback: EventCallback)

The observer can be removed by calling the off method on the property and passing the callback function. Alternatively, you can call off() without any arguments to remove all observers.

public off(callback?: EventCallback)

Example:

const rive = new rive.Rive({
    autoBind: true,
    onLoad: () => {
        // Access the current instance that was auto-bound
        let vmi = rive.viewModelInstance;
        const numberProperty = vmi.number("My Number Property");
        // Observe
        numberProperty.on((event) => {
            console.log(event.data);
        });
        // Remove all listener when done
        numberProperty.off();
    }
});

Images

Image property support is in Early Access and is not yet available for production. Runtime support may be limited or unavailable. See Feature Support for updates.

Image properties let you set and replace raster images at runtime, with each instance of the image managed independently. For example, you could build an avatar creator and dynamically update features — like swapping out a hat — by setting a view model’s image property.

// Create a RiveImageAsset from data
let data = Data(...)
var image = RiveImageAsset(data: data)! // This can return nil if the data is not a valid image

// Or, create a RiveImageAsset from a UIImage
image = RiveImageAsset(image: UIImage(named: "my_image")!, format: .png)! // This can return nil if the image is not a valid jpg or png image

let imageProperty = viewModelInstance.imageProperty(fromPath: "image")!

// Once you have your data binding view model instance, you can set the image property value
imageProperty.setValue(image)

// You can also pass nil to clear the image
imageProperty.setValue(nil)

Lists

List property support is in Early Access and is not yet available for production. Runtime support may be limited or unavailable. See Feature Support for updates.

List properties let you manage a dynamic set of view model instances at runtime. For example, you can build a TODO app where users can add and remove tasks in a scrollable Layout.

A single list property can include different view model types, with each view model tied to its own Nested Artboard, making it easy to populate a list with a varity of Nested Artboards.

With list properties, you can:

  • Add a new view model instance (optionally at an index)
  • Remove an existing view model instance (optionally by index)
  • Swap two view model instances by index
  • Get the size of a list

For more information on list properties, see the Data Binding List Property editor documentation.

let listProperty = viewModelInstance.listProperty(fromPath: "list")!

// Create a new view model instance and add it to the end of the list
let firstInstance = viewModel.createInstanceByName("First Instance")!
listProperty.add(firstInstance)

// Create a new view model instance and add it to the beginning of the list
let secondInstance = myViewModel.createInstanceByName("Second Instance")!
listProperty.add(secondInstance, atIndex: 0)

// Swap the first and second instances
listProperty.swapInstance(atIndex: 0, withInstanceAtIndex: 1)

// Remove both instances
listProperty.removeInstance(secondInstance)
listProperty.removeInstance(atIndex: 0)

// Get and print the size of the list
print(listProperty.size) // Prints 0

Enums

Enums properties come in two flavors: system and user-defined. In practice, you will not need to worry about the distinction, but just be aware that system enums are available in any Rive file that binds to an editor-defined enum set, representing options from the editor’s dropdowns, where user-defined enums are those defined by a designer in the editor.

Enums are string typed. The Rive file contains a list of enums. Each enum in turn has a name and a list of strings.

const rive = new rive.Rive({
    onLoad: () => {
        const enums = rive.enums();

        console.log(enums);
    }
});

Examples

See this video for an intro to data binding using the Web runtime along with this CodeSandbox example.