diff --git a/docs.json b/docs.json
index 462bd572..bff9cb2f 100644
--- a/docs.json
+++ b/docs.json
@@ -304,8 +304,8 @@
"scripting/protocols/test-scripts"
]
},
- "scripting/data-binding",
"scripting/script-inputs",
+ "scripting/data-binding",
"scripting/pointer-events",
{
"group": "Debugging",
diff --git a/images/scripting/referece-secondary-view-model.png b/images/scripting/referece-secondary-view-model.png
new file mode 100644
index 00000000..608f8254
Binary files /dev/null and b/images/scripting/referece-secondary-view-model.png differ
diff --git a/images/scripting/script-input.png b/images/scripting/script-input.png
index 3d65c8e5..a1412281 100644
Binary files a/images/scripting/script-input.png and b/images/scripting/script-input.png differ
diff --git a/images/scripting/secondary-view-model.png b/images/scripting/secondary-view-model.png
new file mode 100644
index 00000000..70c7d99f
Binary files /dev/null and b/images/scripting/secondary-view-model.png differ
diff --git a/scripting/data-binding.mdx b/scripting/data-binding.mdx
index c0d3d8d8..b95afd26 100644
--- a/scripting/data-binding.mdx
+++ b/scripting/data-binding.mdx
@@ -11,12 +11,16 @@ For a conceptual overview of View Models and how they drive your graphic,
see [View Models & Data Binding](/editor/data-binding/overview).
-## View Models
+## Accessing View Models
-There are three ways that a script can gain access to a View Model and its properties:
+There are two ways that a script can gain access to a View Model and its properties:
- Accessing View Models through [Context](#context)
-- Data binding a [View Model as an input](#view-models-as-inputs)
-- Data [binding view model properties to inputs](#binding-inputs)
+- [View Models as an inputs](#view-models-as-inputs)
+
+
+If you only need to read, not set view model properties, you can data bind view model property values to script inputs.
+For more information see [Data Binding Inputs](/scripting/script-inputs#data-binding-inputs).
+
### Context
@@ -28,22 +32,33 @@ fire triggers, listen for triggers, and subscribe to value changes.
In addition to view models, [Context](/scripting/api-reference/interfaces/context) gives you access to named assets and update scheduling.
-```lua {5, 8, 11, 12}
-type MyNode = {}
+```lua
+type GetContexts = {}
-function init(self: MyNode, context: Context): boolean
+function init(self: GetContexts, context: Context): boolean
-- Get the view model from the node's immediate context.
- local vmi = context:viewModel()
+ local mainVmi = context:viewModel()
- -- Get the root view model
+ --- Get the root view model
local rootVmi = context:rootViewModel()
+ --- Get a global view model
+ local globalVmi = context:globalViewModel('MyGlobalVM')
+
-- Get the view model from the parent node.
- local parentDC = dc:parent()
- local parentVmi = dc:viewModel()
+ local dc = context:dataContext()
+ if dc then
+ local parentDC = dc:parent()
+
+ if parentDC then
+ local parentVmi = parentDC:viewModel()
+ end
+ end
+
+ return true
end
-return function(): Node
+return function(): Node
return {
init = init,
}
@@ -52,43 +67,50 @@ end
### View Models as Inputs
-You can create a [Script Input](/scripting/script-inputs) that can be bound to a view model. This allows you to read values (strings, enums, lists, etc.), set values, fire triggers, listen for triggers, and subscribe to value changes.
+You can create an [Input](/scripting/script-inputs) that accepts a view model instance, which gives the script access to its properties.
-```lua {3,7,15}
-type MyNode = {
- -- This input expects a view model instance of type Character
- character: Input
-}
+
+
+ This is in addition to your main view model.
-function init(self: MyNode, context: Context): boolean
- local vmi = self.character
-end
+ In this example, we call it `MenuVM` and give it a `string` property called `title`.
-return function(): Node
- return {
- init = init,
- -- Initialize with `late()` so the value
- -- can be provided by the editor at runtime.
- character = late(),
- }
-end
-```
+ 
+
+
+ To be able to reference the instance of `MenuVM` in your script, we need to create a `myMenu` property of type `MenuVM` in the main view model.
-For a detailed explanation, see [View Models Inputs](/scripting/script-inputs#view-model-inputs).
+ 
+
+
+ In your script add a new input of type `Data.MenuVM`.
-### Binding Inputs
+ ```lua {3,8,17}
+ -- Define the script's data and inputs.
+ type ScriptInputs = {
+ myMenu: Input,
+ }
-If you only need to read, not set view model properties, you can data bind view model property values to script inputs.
-For more information see [Data Binding Inputs](/scripting/script-inputs#data-binding-inputs).
+ -- Called once when the script initializes.
+ function init(self: ScriptInputs, context: Context): boolean
+ print(self.myMenu.title.value)
-### Nested View Models
-
-To reference a nested view model, use `getViewModel`.
+ return true
+ end
-```lua
-local vmi = context:viewModel(),
-local dateVmi = vmi:getViewModel('dateViewModel')
-```
+ -- Return a factory function that Rive uses to build the Node instance.
+ return function(): Node
+ return {
+ init = init,
+ myMenu = late(),
+ }
+ end
+ ```
+
+
+ Select the script in your hierarchy and in the Property Group panel of the Inspector, set the `myMenu` input to `menu`.
+
+
## Reading and Setting Properties
@@ -133,85 +155,122 @@ end
Use `addListener` to listen for triggers or changes to view model properties.
-```lua {17,23}
-type MyNode = {}
+```lua {19,22,23,24}
+-- Define the script's data and inputs.
+type ScriptInputs = {
+ menu: Input,
+}
-function handleHoursChanged()
- print('hours changed!')
+function onTitleChange()
+ print('changed')
end
-function handleTriggerFired()
- print('Fire!')
+function onTitleChangeWithParam(self: ScriptInputs)
+ print('changed', self.menu.title.value)
end
-function init(self: MyNode, context: Context): boolean
- local vmi = context:viewModel()
+-- Called once when the script initializes.
+function init(self: ScriptInputs, context: Context): boolean
+ local title = self.menu.title
- local hours = vm:getNumber('hours')
- if hours then
- -- handleHoursChanged is called whenever the hours value changes
- hours:addListener(handleHoursChanged)
- end
+ -- When title changes, call onTitleChange
+ title:addListener(onTitleChange)
- local trigger = vm:getTrigger('triggerProperty')
- if trigger then
- -- handleTriggerFired is called whenever the trigger is fired
- trigger:addListener(handleTriggerFired)
- end
+ -- When title changes, call onTitleChangeWithParam with an argument of self
+ title:addListener(self, onTitleChangeWithParam)
return true
end
-return function(): Node
+-- Return a factory function that Rive uses to build the Node instance.
+return function(): Node
return {
init = init,
+ score = 0,
+ menu = late(),
+ newArtboard = late(),
}
end
+
```
### Remove a Listener
Always remove listeners when they are no longer needed to avoid memory leaks.
-```lua highlight={17}
-type MyNode = {}
-
-function init(self: MyNode, context: Context): boolean
- local vmi = context:viewModel()
+```lua highlight={14}
+-- Define the script's data and inputs.
+type ScriptInputs = {
+ menu: Input,
+}
- if not vmi then
- print('No view model found')
- return false
- end
+-- Anchor-form listener callback: receives the same object passed as the
+-- anchor to addListener (here, the `title` Property itself).
+function onTitleChangeWithParam(title: Property)
+ print('title changed:', title.value)
- local hours = vmi:getNumber('hours')
- if hours then
- local function handleHoursChanged()
- print('hours changed!')
+ -- Unsubscribe using the matching anchor-form overload (self, anchor, callback).
+ -- The simpler (self, callback) overload requires a zero-argument callback,
+ -- which is why using it here caused a type error against this 1-argument function.
+ title:removeListener(title, onTitleChangeWithParam)
+end
- -- Remove the event listener
- hours:removeListener(handleHoursChanged)
- end
+-- Called once when the script initializes.
+function init(self: ScriptInputs, context: Context): boolean
+ local title = self.menu.title
- -- handleHoursChanged is called whenever the hours value changes
- hours:addListener(handleHoursChanged)
- end
+ -- When title changes, call onTitleChangeWithParam with the anchor (title) as its argument.
+ title:addListener(title, onTitleChangeWithParam)
return true
end
-return function(): Node
+-- Return a factory function that Rive uses to build the Node instance.
+return function(): Node
return {
init = init,
- hours = late(),
- trigger = late(),
+ menu = late(),
}
end
-
```
## Creating a View Model Instance
-Coming soon
+
+```lua highlight={10}
+-- Define the script's data and inputs.
+type ScriptInputs = {
+ menu: Input,
+}
+
+-- Called once when the script initializes.
+function init(self: ScriptInputs, context: Context): boolean
+ -- Create a brand new MenuVM ViewModel instance programmatically
+ -- (instead of relying on the data-bound `menu` input from the editor).
+ local newMenuInstance = Data.MenuVM.new()
+
+ local newTitle = newMenuInstance:getString('title')
+ if newTitle then
+ newTitle.value = 'Created at runtime'
+ end
+
+ -- Swap self.menu to use the instance we just created programmatically.
+ self.menu = newMenuInstance
+
+ local title = self.menu.title
+
+ print('menu title after programmatic creation:', title.value)
+
+ return true
+end
+
+-- Return a factory function that Rive uses to build the Node instance.
+return function(): Node
+ return {
+ init = init,
+ menu = late(),
+ }
+end
+```
diff --git a/scripting/script-inputs.mdx b/scripting/script-inputs.mdx
index 09e3c8a8..4d88f281 100644
--- a/scripting/script-inputs.mdx
+++ b/scripting/script-inputs.mdx
@@ -5,10 +5,16 @@ description: ""
---
import { Demos } from '/snippets/demos.jsx'
+import { Marketplace } from '/snippets/marketplace.mdx'
+import { YouTube } from '/snippets/youtube.mdx'
Scripted Inputs are the bridge between your scripts and the Rive editor, allowing you to customize and control script behavior through custom input fields.
-By defining inputs in your scripts, you expose configurable properties — like numbers, colors, booleans, and artboard components — that appear directly in the Rive interface. This means you can write the logic once in a script, and then experiment freely with values, animate properties over time, bind data from external sources, and reuse the same script across multiple instances with different configurations. Inputs transform static scripts into flexible, designer-friendly tools that enable true collaboration and rapid iteration.
+By defining inputs in your scripts, you expose configurable properties — like numbers, colors, booleans, view model instances, and artboard components — that appear directly in the Rive interface. This means you can write the logic once in a script, and then experiment freely with values, animate properties over time, bind data from external sources, and reuse the same script across multiple instances with different configurations.
+
+Inputs transform static scripts into flexible, designer-friendly tools that enable true collaboration and rapid iteration.
+
+
## Defining Inputs
@@ -16,49 +22,25 @@ To make new script inputs, add them to the type and set the defaults in the scri
```lua
-- Define the script's data and inputs.
--- These properties will be available in `self`
-type MyNode = {
- myNumber: Input,
- myColor: Input,
- -- This input expects a View Model named Points
- myViewModel: Input,
- -- This input expects an Artboard with a View Model named Points
- myArtboard: Input>,
- -- This will be accessible via self, but not in the inputs panel
- myString: string,
+type ScriptInputs = {
+ score: Input,
+ menu: Input,
+ newArtboard: Input>,
}
-function init(self: MyNode): boolean
- print("myString", self.myString)
- print("myNumber", self.myNumber)
- print("myColor", self.myColor)
- print("myViewModel value", self.myViewModel.someString.value)
- print("myViewModel value", self.myArtboard.data.someEnum.value)
-
- return true
-end
-
-return function(): Node
+-- Return a factory function that Rive uses to build the Node instance.
+return function(): Node
return {
- init = init,
- draw = draw,
- myString = "Rive for president!"
- -- Sets default value when creating a new instance of the script
- -- This will be overridden by a value set in the script's inputs
- myNumber = 0,
- myColor = Color.rgba(255, 255, 0, 255), -- 0xFFFFFF00
-
- -- Use late() to mark this input as assigned at runtime
- myViewModel = late(),
- myArtboard = late()
+ score = 0,
+ menu = late(),
+ newArtboard = late(),
}
end
-
```
-
- Using inputs, instances of Artboards can be added to your scene at runtime. See [Instantiating Components](/scripting/protocols/node-scripts#instanting-components).
-
+
## Setting Input Values
@@ -66,27 +48,17 @@ To access the input properties in the right sidebar of the editor, select your [

-
## Data Binding Inputs
You can use [Data Binding](/editor/data-binding/overview) to control input values at runtime.
-
- Inputs can control scripts, but scripts can't change the value of inputs.
-
- If you need to control a view model property from your script, access the [view models through context](/scripting/data-binding#context) or [View Model Inputs](#view-model-inputs).
-
-
To data bind an input, right-click the input field in right sidebar, choose Data Bind, and select a
property.

+
## Listening for Changes to Inputs
The `update` function fires every time any input changes.
@@ -96,98 +68,3 @@ function update(self: MyNode)
print('An update changed')
end
```
-
-You can also listen for changes to specific properties:
-
-```lua
-function handleMyStringChanged()
- print('myString changed!')
-end
-
-function handleMyNumberChanged(myNumber: number)
- print('myNumber changed!', myNumber)
-end
-
-function init(self: MyNode): boolean
- -- handleMyStringChanged fires when self.myString changes
- local myString = self.myString
- myString:addListener(handleMyStringChanged)
-
- -- Pass a parameter to the handleMyStringChanged callback
- local myNumber = self.myNumber
- myNumber:addListener(myNumber.value, handleMyNumberChanged)
-
- return true
-end
-```
-
-
-## View Model Inputs
-
-View Model Inputs let your script read from and write to View Model properties. These properties can control any element in your Rive scene via (See [Data Binding](/editor/data-binding/overview)).
-
-
- The easiest way to access view models in your scripts is [through context](/scripting/data-binding#context).
-
-
-### Setting Up Your View Model
-
-**In this example:**
-
-- The `Main` view model has a property named `character`.
-- The `character` property is itself a `Character` view model.
-- The `Character` view model contains two number properties (x and y) that you want to control from your script.
-
-
-
-### Defining a View Model Input
-
-Inside your script, declare a new input whose type matches the nested view model you want to reference (`Data.` + the name of your nested view model).
-
-In this case, the Character view model type becomes `Data.Character`.
-
-```lua
-type MyNode = {
- -- This input expects a view model instance of type Character
- character: Input
-}
-
-return function(): Node
- return {
- init = init,
- advance = advance,
- draw = draw,
- -- Initialize with `late()` so the value
- -- can be provided by the editor at runtime.
- character = late(),
- }
-end
-```
-
-### Connecting the Input in the Editor
-
-1. Select your script in the Scene panel (or the converter if you're using a [Converter](/scripting/protocols/converter-scripts) script)
-2. In the right sidebar, look for the Property Group section
-3. You’ll see a dropdown for your character input
-4. Select your nested `character` property from the Main view model
-
-
-
-### Reading and Writing View Model Properties
-
-Once connected, you can access the nested view model directly from your script:
-
-```lua
-function moveCharacter(self: MyNode)
- print('Current x: ', self.character.x.value)
- self.character.x.value = 10
-end
-```
-
-Because character is a view model instance, you can access all of its public properties:
-
-```lua
-self.character..value
-```
-
-