DEPRECATION NOTICE: This entire page documents the legacy Text
manipulation system. For new projects: Use Data
Binding instead. For existing projects: Plan to
migrate from direct text run manipulation to Data Binding as soon as possible.
For more information on designing and animating Text, please refer to the editor’s text section:
Please ensure you’re on the correct runtime version with support for Rive Text; see Feature Support.
Read/Update Text Runs at Runtime
⚠️ LEGACY CONTENT WARNING: The following sections document the deprecated
Text manipulation system. This content is provided for legacy support
only. New implementations should use Data Binding.
If you intend to update a text run at runtime it’s important to manually enter a unique name for the run in the editor:
And then export the name: right-click and select Export name
You can identify an exported component if it’s surrounded by square brackets. This makes it possible for the run to be “discoverable” at runtime by its name. For more information, see Exporting for Runtime.
If the name is not set manually in the editor the name will not be part of
the exported .riv (to reduce file size).
Web
React
React Native
Flutter
Apple
Android
Examples
High-level API usage
Reading TextTo read a given text run text value at any given time, reference the .getTextRunValue() API on the Rive instance:public getTextRunValue(textRunName: string): string | undefined
Supply the text run name, and you’ll have the Rive text run value returned, or undefined if the text run could not be queried.Setting TextTo set a given text run value at any given time, reference the .setTextRunValue() API on the Rive instance:public setTextRunValue(textRunName: string, textRunValue: string): void
Supply the text run name and a second parameter, textValue, with a String value that you want to set the new text value for if the text run can be successfully queried on the active artboard.Example Usage
import {Rive} from '@rive-app/canvas'
const r = new Rive({
src: "my-rive-file.riv"
artboard: "my-artboard-name",
autoplay: true,
stateMachines: "State Machine 1",
onLoad: () => {
console.log("My design-time text is, ", r.getTextRunValue("MyRun"));
r.setTextRunValue("MyRun", "New text value");
},
})
Low-level API usage
Get a reference to the Rive Artboard, find a text run by a given name, and get/update the text value property.import RiveCanvas from '@rive-app/canvas-advanced';
const bytes = await (
await fetch(new Request('./my-rive-file.riv'))
).arrayBuffer();
const myRiveFile = await rive.load(new Uint8Array(bytes));
const artboard = myRiveFile.defaultArtboard();
const textRun = artboard.textRun("MyRun"); // Query by the text run name
console.log(`My design-time text is ${textRun.text}`);
textRun.text = "Hello JS Runtime!";
...
Examples
Reading Text
To read a given text run text value at any given time, reference the .getTextRunValue() API on the rive instance returned from useRive:public getTextRunValue(textRunName: string): string | undefined
Supply the text run name, and you’ll have the Rive text run value returned, or undefined if the text run could not be queried.Setting Text
To set a given text run value at any given time, reference the .setTextRunValue() API on the rive instance returned from useRive:public setTextRunValue(textRunName: string, textRunValue: string): void
Supply the text run name and a second parameter, textValue, with a String value that you want to set the new text value for if the text run can be successfully queried on the active artboard.Example Usage
import { useRive } from '@rive-app/canvas';
const MyTextComponent = () => {
const {rive, RiveComponent} = useRive({
src: "my-rive-file.riv",
artboard: "New Artboard",
stateMachines: "State Machine 1",
autoplay: true,
});
// Cannot query for the text run immediately, you have to wait until `rive`
// has value and has instantiated before querying or setting text run values
useEffect(() => {
if (rive) {
console.log("Rive text was initially: ", rive.getTextRunValue("MyRun"));
rive.setTextRunValue("MyRun", "New Text!!");
console.log("Rive text is now: ", rive.getTextRunValue("MyRun"));
}
}, [rive]);
return (
<RiveComponent />
);
};
Setting Text via Rive Ref
To set a given text run value at any given time, reference the .setTextRunValue() API on the Rive ref:setTextRunValue: (textRunName: string, value: string) => void;
Supply the text run name and a second parameter, textValue, with a String value that you want to set the new text value for.Example Usage
export default function DynamicText() {
const riveRef = useRef<RiveRef>(null);
const handleInputChange = (e: string) => {
// Set the TextRun value of the 'name' TextRun
// The name must exist else an error will be thrown
riveRef.current?.setTextRunValue('name', e);
};
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<ScrollView>
<Rive
ref={riveRef}
resourceName="hello_world_text"
stateMachineName="State Machine 1"
/>
<TextInput
onChangeText={handleInputChange}
defaultValue="world"
/>
</ScrollView>
</SafeAreaView>
);
}
Get/set the text run value on an Artboard instance.We recommend using Data Binding instead to update text at runtime. final controller = RiveWidgetController(riveFile);
final artboard = controller.artboard;
// Get a text run value
artboard.getText(value)
// Optional path parameter to get a text run value at a component instance level
artboard.getText(value, path: path)
// Set a text run value
artboard.setText(value)
// Optional path parameter to set a text run value at a component instance level
artboard.setText(value, path: path)
Reading Text
To read a given text run text value at any given time, reference the .getTextRunValue() API on the RiveViewModel:open func getTextRunValue(_ textRunName: String) -> String?
Supply the text run name and you’ll have the Rive text run value returned, or nil if the text run could not be queried.Setting Text
To set a given text run value at any given time, reference the .setTextRunValue() API on the RiveViewModel:open func setTextRunValue(_ textRunName: String, textValue: String) throws
Supply the text run name and a second parameter, textValue, with a String value that you want to set the new text value for.If the supplied textRunName Rive text run cannot be queried on the active artboard, Rive will throw a RiveError.textValueRunError that you may need to catch and handle gracefully in your application.
Example Usage
@State private var userInput: String = ""
@State private var rvm = RiveViewModel(fileName: "my-rive-file")
var body: some View {
VStack(spacing: 20) {
Text("Enter text:")
.font(.headline)
TextField("Enter text...", text: $userInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onChange(of: userInput, perform: { newValue in
if (!newValue.isEmpty) {
try! rvm.setTextRunValue("MyTextRunName", textValue: userInput)
}
})
rvm.view()
}
}
Reading Text via RiveAnimationView
To read a given text run text value at any given time, reference the .getTextRunValue() API on the RiveAnimationView:fun getTextRunValue(textRunName: String): String? = try
Supply the text run name and you’ll have the Rive text run value returned, or null if the text run could not be queried.Setting Text via RiveAnimationView
To set a given text run value at any given time, reference the .setTextRunValue() API on the RiveAnimationView:fun setTextRunValue(textRunName: String, textValue: String)
Supply the text run name and a second parameter, textValue, with a String value that you want to set the new text value for.If the supplied textRunName Rive text run cannot be queried on the active artboard, Rive will throw a RiveException that you may need to catch and handle gracefully in your application.
Reference to Rive TextRun
You can also choose to query the active Artboard for the Rive text run reference and get/set a text property manually.private val textRun by lazy(LazyThreadSafetyMode.NONE) {
yourRiveAnimationView.artboardRenderer?.activeArtboard?.textRun("name")
}
Example Usageimport android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import app.rive.runtime.kotlin.RiveAnimationView
class DynamicTextActivity : AppCompatActivity(), TextWatcher {
private val animationView by lazy(LazyThreadSafetyMode.NONE) {
findViewById<RiveAnimationView>(R.id.dynamic_text)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dynamic_text)
val editText = findViewById<EditText>(R.id.text_run_value)
editText.addTextChangedListener(this)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// get the current value of the reference
animationView.getTextRunValue("name")
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// update the reference
animationView.setTextRunValue("name", s.toString())
}
}
Read/Update Nested Text Runs at Runtime
⚠️ DEPRECATED FEATURE: Nested Text Runs are part of the legacy Text
manipulation system. Use Data Binding instead for controlling component
text properties at runtime.
It’s possible to set nested text runs at runtime—text that is not on the main artboard but on a Component. To set a nested text run, you’ll need to take note of the path where the input exists at an artboard level.
For example, to get/set the text run named button_text on the Button artboard, you need to provide the correct path.
Setting Nested Text Runs
The artboard names here are:
- Main -> NestedArtboard -> Button
However, the path is determined based on the names set in the hierarchy:
- ArtboardWithUniqueName -> ButtonWithUniqueName
The path is then: ArtboardWithUniqueName/ButtonWithUniqueName
Be sure to mark the components and text runs as exported.
Export
component name
Do not use ”/” in the name for your components, as that will break the search
functionality at runtime.
Web
React
Flutter
Apple
Android
Unity
Examples
High-level API usage
Reading TextTo read a given text run text value at a specific path, reference the .getTextRunValueAtPath() API on the Rive instance:public getTextRunValueAtPath(textRunName: string, path: string): string | undefined
Supply the text run name and the path where it is located, and you’ll have the Rive text run value returned, or undefined if the text run could not be queried.Setting TextTo set a given text run value at a specific path, reference the .setTextRunValueAtPath() API on the Rive instance:public setTextRunValueAtPath(textRunName: string, textRunValue: string, path: string): void
Supply the textRunName, the new textValue, and the path where the run is located at a component instance level.Example Usage
import {Rive} from '@rive-app/canvas'
const r = new Rive({
src: "my-rive-file.riv"
artboard: "my-artboard-name",
autoplay: true,
stateMachines: "State Machine 1",
onLoad: () => {
console.log("My design-time text is, ", r.getTextRunValueAtPath("MyRun", "path/to/run"));
r.setTextRunValueAtPath("MyRun", "New text value", "path/to/run");
},
})
Examples
Reading Text
To read a given text run text value at a specific path, reference the .getTextRunValueAtPath() API on the rive instance returned from useRive:public getTextRunValueAtPath(textRunName: string, path: string): string | undefined
Supply the text run name and the path, and you’ll have the Rive text run value returned, or undefined if the text run could not be queried.Setting Text
To set a given text run value at a specific path, reference the .setTextRunValueAtPath() API on the rive instance returned from useRive:public setTextRunValueAtPath(textRunName: string, textRunValue: string, path: string): void
Supply the textRunName, the new textRunValue , and the component path where it is located.Example Usage
import { useRive } from '@rive-app/canvas';
const MyTextComponent = () => {
const {rive, RiveComponent} = useRive({
src: "my-rive-file.riv",
artboard: "New Artboard",
stateMachines: "State Machine 1",
autoplay: true,
});
// Cannot query for the text run immediately, you have to wait until `rive`
// has value and has instantiated before querying or setting text run values
useEffect(() => {
if (rive) {
console.log("Rive text was initially: ", rive.getTextRunValueAtPath("MyRun", "path/to/run"));
rive.setTextRunValueAtPath("MyRun", "New Text!!", "path/to/run");
console.log("Rive text is now: ", rive.getTextRunValue("MyRun, "path/to/run");
}
}, [rive]);
return (
<RiveComponent />
);
};
Get/set a nested text run value on an Artboard instance.We recommend using Data Binding instead to update text at runtime. final controller = RiveWidgetController(riveFile);
final artboard = controller.artboard;
//Use the path parameter to get a text run value at a component instance level
artboard.getText(value, path: path)
// Use the path parameter to set a text run value at a component instance level
artboard.setText(value, path: path)
Reading Text
Similar to reading text from a text run within a parent artboard, you can set the value of a text run within a component instance using the following API:open func getTextRunValue(_ textRunName: String, path: String) -> String?
Setting Text
Additionally, similar to setting text within a parent artboard, you can read the value of a text run within a component instance using the following API:open func setTextRunValue(_ textRunName: String, textValue: String, path: String) throws
Example:
@State private var userInput: String = ""
@State private var rvm = RiveViewModel(fileName: "my-rive-file")
var body: some View {
VStack(spacing: 20) {
Text("Enter text:")
.font(.headline)
TextField("Enter text...", text: $userInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onChange(of: userInput, perform: { newValue in
if (!newValue.isEmpty) {
try! rvm.setTextRunValue("MyTextRunName", textValue: userInput, path: "Artboard/NestedArtboard")
}
})
rvm.view()
}
}
Reading Text via RiveAnimationView
To read a given text run text value at any given time, reference the .getTextRunValue() API on the RiveAnimationView:fun getTextRunValue(textRunName: String, path: String): String? = try
Supply the text run name and the path to where it exists and you’ll have the Rive text run value returned, or null if the text run could not be queried.Setting Text via RiveAnimationView
To set a given text run value at any given time, reference the .setTextRunValue() API on the RiveAnimationView:fun setTextRunValue(textRunName: String, textValue: String, path: String)
Supply the text run name, a second parameter, textValue, with a String value that you want to set the new text value for, and the path value to where the text run exists at an artboard level.If the supplied textRunName Rive text run cannot be queried on the active artboard, Rive will throw a RiveException that you may need to catch and handle gracefully in your application.Reference to Rive TextRun
You can also choose to query the active Artboard for the Rive text run reference and get/set a text property manually.private val textRun by lazy(LazyThreadSafetyMode.NONE) {
yourRiveAnimationView.artboardRenderer?.activeArtboard?.textRun("name", "path/to/artboard")
}
To set the button_text text run value on the nested button artboard, the code snippet would look like this for the example above:m_file = Rive.File.Load(asset);
m_artboard = m_file.Artboard(0);
// Set a nested text run value
m_artboard.SetTextRunValueAtPath("button_text", "ArtboardWithUniqueName/ButtonWithUniqueName", "Click Me!");
// Get a nested text run value
string buttonText = m_artboard.GetTextRunValueAtPath("button_text", "ArtboardWithUniqueName/ButtonWithUniqueName");
API Methods:
void SetTextRunValueAtPath(string runName, string path, string value)
string GetTextRunValueAtPath(string runName, string path)
These methods allow you to interact with nested text runs by specifying the run name and the path within the artboard hierarchy. With this approach, you can access deeper nested text runs by continuing to construct the path, for example: "Artboard-Nested-Level1/Arboard-Nested-Level2/Artboard-Nested-Level3", and so on.
Semantics for Accessibility
We recommend using Data Binding instead as
you’ll be able to do a two way text binding.
As Rive Text does not make use of the underlying platform text APIs, additional steps need to be taken to ensure it can be read by screen readers.
The following code snippets provide guidance on how to add semantic labels to your Rive animations. Please see the respective platform/SDKs developer documentation for additional information regarding accessibility concerns.
See Flutter’s documentation on Accessibility for more information. In this example you’ll use the Semantics widget to provide a label that reflects the current value of the Rive Text component....
final controller = RiveWidgetController(riveFile);
final textValue = controller.artboard.getText('some_text_run_name').value;
String semanticLabel = textValue;
...
Semantics(
label: semanticLabel,
child: RiveWidget(controller: controller);
),
Adding ARIA LabelAt a minimum - if it is important to convey the text value displayed in the Rive animation to all users, add an aria-label to the <canvas> element with the text value from the animation. Screen readers may read this label out immediately as it parses out the DOM contents. You’ll also want to add role="img" to the <canvas> element as well.<canvas
id="rive-canvas"
width={500}
height={500}
role="img"
aria-label="Hello friend, welcome to my page"
></canvas>
Adding ARIA Live Region
While ARIA labels are a direct method to manage a textual label for screen readers to read out as it parses web content, using an ARIA live region allows you a way to control when screen readers read out dynamic text content.Live regions are useful in cases where the text content in your Rive graphic becomes visible or changes on a particular state in a state machine, and you want screen readers to pick up on text changes. Another use case is when you only want screen readers to read your Rive text content when the <canvas> is scrolled into view.Read more on ARIA live regions here.Example: Rating Graphic
Imagine you’re displaying an interactive Rive graphic that allows users to choose a rating from 1-5 stars. Users clicking on the different stars can visually see the state machine in action with animations to see what star they click, but screen readers may need a way to announce what selection was chosen as other users navigate the canvas with keyboard controls, for example.The HTML for this might look like the following:<canvas
role="img"
tabindex="0"
aria-describedby="rating-animation-live"
id="canvas"
></canvas>
<p id="rating-animation-live" class="sr-only" aria-live="assertive">
An interactive rating simulation. Use the left and right arrow keys to
select a rating
</p>
Note that the <canvas> element has an aria-describedby attribute whose value matches the id of the <p> below it, #rating-animation-live. This allows the <p> block content to describe the <canvas> element. And similar to using aria-label, we have to add the role="img" attribute to the canvas as well. The aria-live="assertive" attribute describes how to interrupt the screen reader’s flow of reading content based on when the content within this <p> changes.Let’s take a look at what the JS might look like using the Rive Web (JS) runtime:const rive = require("@rive-app/canvas");
const dynamicTextEl = document.getElementById("rating-animation-live");
const r = new rive.Rive({
src: "rating.riv",
canvas: document.getElementById("canvas"),
stateMachines: "State Machine 1",
autoplay: true,
onLoad: () => {
r.setTextRunValue("RatingRun", "0");
r.resizeDrawingSurfaceToCanvas();
// See CodeSandbox link above for further functionality
},
onStateChange: (e) => {
const name = e.data[0];
const ratingStr = name.charAt(0);
const ratingNum = parseInt(ratingStr);
if (!isNaN(ratingNum)) {
const ratingString = name
.split("_")
.reduce((acc, temp) => {
return (acc += ` ${temp}`);
}, "")
.trim();
r.setTextRunValue("RatingRun", ratingStr);
dynamicTextEl.innerHTML = `Rating: ${ratingString}`;
}
}
});
In the above snippet, we’ve created an instance of Rive, and as the state changes in the state machine, we’re dynamically updating the contents of the live region (represented by dynamicTextEl) with the string rating. Due to the live region having the property of aria-live="assertive", screen readers should read off the new dynamic text content.
Additional Resources:
Fallback Fonts
When drawing text, not all glyphs (characters) may be available, such as custom fonts that may not support multiple languages, or if your embedded fonts have been set to not contain all glyphs. In these cases, it may be best to have a font that is used when a missing glyph is encountered. Some platforms have support for fallback fonts, which allow the use of system fonts as fallbacks (which should contain most missing glyphs).
On iOS and Android, sizes are ignored; instead, supplied fonts are used to
render glyphs that match the styling and animation of a text run at runtime.
As of v6.1, on iOS and macOS, various options for fallbacks can be used. The Apple runtime provides helpers for selecting system fonts based on requested styling. Additionally, UIFonts / NSFonts can be used directly.A default system font of regular weight and width will be used if no fallbacks have been registered.
// Early in your app lifecycle, call something similar to the following:
RiveFont.fallbackFonts = [
RiveFallbackFontDescriptor(), // Use a default system font
RiveFallbackFontDescriptor(design: .default, weight: .bold, width: .expanded), // Use a bold, expanded system font
UIFont(name: "...", size: 20)!
]
// Alternatively, you can supply different fonts based on style at runtime
As of v6.4, on iOS and macOS, you can utilize a more dynamic callback-based API for returning various fonts depending on the style of any missing characters, as styled in a text run.// As with the similar fallbackFonts API, you utilize Rive helper types
// or native UIFont/NSFont types
RiveFont.fallbackFontsCallback = { style in
switch style.weight {
case .thin: return [
RiveFallbackFontDescriptor(weight: .thin),
UIFont.systemFont(ofSize: 20, weight: .thin)
]
case .bold: return [
RiveFallbackFontDescriptor(weight: .bold),
UIFont.systemFont(ofSize: 20, weight: .bold)
]
default: return [
RiveFallbackFontDescriptor(),
UIFont.systemFont(ofSize: 20)
]
}
}
// Alternatively, you can use the raw weight to return various fonts as well
RiveFont.fallbackFontsCallback = { style in
switch style.rawWeight {
case 100: return [
RiveFallbackFontDescriptor(weight: .thin),
UIFont.systemFont(ofSize: 20, weight: .thin)
]
case 700: return [
RiveFallbackFontDescriptor(weight: .bold),
UIFont.systemFont(ofSize: 20, weight: .bold)
]
default: return [
RiveFallbackFontDescriptor(),
UIFont.systemFont(ofSize: 20)
]
}
}
As of v9.12.0, various options for fallback fonts can be used on Android.If no fallback fonts are registered, a default system font (“sans-serif”) with a regular weight (400, NORMAL) and normal style will be used.
The Fonts class provides ways to handle and customize fonts, including retrieving system fonts, defining font options, and finding fallback fonts based on specific characteristics.1. Setting a Fallback Font
With v9.12.0, the runtime provides a new API to match missing fonts against a specific weight by extending the FontFallbackStrategy interface.This interface contains a single method:fun getFont(weight: Fonts.Weight): List<FontBytes>
Implementers need to override this method. The user’s implementation must then be set as the current fallback strategy via FontFallbackStrategy.stylePicker.Example:class FontFallback : AppCompatActivity(), FontFallbackStrategy {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set the fallback strategy
FontFallbackStrategy.stylePicker = this
}
override fun getFont(weight: Fonts.Weight): List<FontBytes> {
val desiredWeight = weight.weight
val fonts = listOf(
Fonts.FontOpts(
familyName = "sans-serif",
weight = Fonts.Weight(weight = desiredWeight) // Find a matching weight font
),
// Non-Latin Unicode fallback
Fonts.FontOpts("NotoSansThai-Regular.ttf")
)
return fonts.mapNotNull {
// Filter out fonts that cannot be found on the system
FontHelper.getFallbackFontBytes(it)
}
}
}
The method returns a list of FontBytes (`ByteArray`). The runtime attempts to match the character using the fonts in the list in a first-in, first-out (FIFO) order.Fallback fonts can also be set using Rive.setFallbackFont(), with optional font preferences defined in Fonts.FontOpts. These fonts are tried only after attempting the ones returned by FontFallbackStrategy.getFont().2. Font.FontOpts - Font Options
Defines the font characteristics when selecting a fallback font.
- Parameters
familyName: Name of the font family (e.g., “Roboto”, “NotoSansThai-Regular.ttf”). Defaults to null
lang: Optional language specification. Defaults to null
weight: Font weight using Fonts.Weight (e.g., Fonts.Weight.NORMAL, Fonts.Weight.BOLD). Default is Weight.NORMAL
style: Font style, either Fonts.Font.STYLE_NORMAL or Fonts.Font.STYLE_ITALIC. Default is STYLE_NORMAL
- Default example
val defaultFontOpts = Fonts.FontOpts.DEFAULT
3. Retrieving a Fallback Font
Use FontHelper.getFallbackFont() to find a suitable fallback font based on specified options. Returns a Fonts.Font object or null if no match is found.Example:val fontOpts = Fonts.FontOpts(familyName = "Roboto", weight = Fonts.Weight.BOLD)
val fallbackFont = FontHelper.getFallbackFont(fontOpts)
4. Getting Font File and Bytes
FontHelper.getFontFile(font: Fonts.Font): Retrieves the file for the specified font.
FontHelper.getFontBytes(font: Fonts.Font): Reads the font file and returns its bytes.
Example:val fontFile = FontHelper.getFontFile(fallbackFont)
val fontBytes = FontHelper.getFontBytes(fallbackFont)
5. Fonts.Weight - Font Weight
Represents the font weight, allowing values from 0 to 1000.
- Predefined Weights
Fonts.Weight.NORMAL (400)
Fonts.Weight.BOLD (700)
Example:val normalWeight = Fonts.Weight.NORMAL
val customWeight = Fonts.Weight.fromInt(500)
6. Fonts.Style - Font Style
Represents the font style, allowing “normal” and “italic”
- Predefined Styles
Fonts.Font.STYLE_NORMAL
Fonts.Font.STYLE_ITALIC
Example:val normalStyle = Fonts.Font.STYLE_NORMAL
val italicStyle = Fonts.Font.STYLE_ITALIC
7. Getting System Fonts
FontHelper.getSystemFonts(): Returns a map of all available system font families.
Example:val systemFonts = FontHelper.getSystemFonts()