# Account Management
Source: https://rive.app/docs/account-admin/account-overview/account-management
Your account settings live at [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
You can also get there by clicking your name (top-left corner) → Manage Account.
From here you can:
* Update your email, password, or profile
* Manage your workspaces and billing
* View notifications
* Manage embed URLs for your files
Your workspaces are listed in the left sidebar. Click "Manage Workspace" next to any workspace to change its plan, update payment, or invite members.
**Questions?** Contact support.
# Billing Changes
Source: https://rive.app/docs/account-admin/account-overview/billing-changes
How to update your card, change your plan, or manage billing for your Rive workspace.
⚠️ If you have a pending change (like a scheduled cancellation), you can't make other billing changes until it's resolved. Contact support if you're stuck.
## Where to Manage Billing
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. Find your workspace under "Workspaces"
3. Click "Manage Workspace"
From here you can:
* **Change Plan** → Switch between Free and Voyager
* **Manage Billing** → Opens Stripe to update your payment card
⚠️ "Create a Workspace" creates a NEW workspace. It does not update your current plan.
***
## Update Your Payment Card
1. Click "Manage Billing" to open the Stripe Customer Portal
2. Go to "Payment Methods"
3. Add your new card
4. Click "Set as default"
5. Remove the old card (optional)
If your workspace was downgraded due to a failed payment, updating your card alone will not restore access. After updating your payment method, click "Upgrade Plan" and select a paid plan again.
Access is typically restored within a few minutes.
***
## Change Your Plan
Click "Change Plan" from the Manage Workspace page to switch between Free and Voyager.
**Have legacy/grandfathered pricing?** Contact support and we'll process the change for you. This includes switching between monthly and annual billing.
***
## If Your Card Keeps Getting Declined
Call your bank first. Common issues:
* International transactions blocked (Rive uses Stripe)
* Bank flagged the charge as suspicious
After your bank clears it, wait 30 minutes and try again. Still not working? Try a different card or contact support.
***
## Multiple Workspaces?
Each workspace has its own billing. Make sure you're managing the right one—check the workspace name at the top of the Manage Workspace page.
***
## For Team Members
Only the workspace owner can update billing. If you're seeing access errors due to a payment issue, let the owner know.
**Questions?** Contact support.
# Cancelling My Plan
Source: https://rive.app/docs/account-admin/account-overview/cancel-my-account
Cancel a workspace's paid plan.
Plans are managed per workspace. Cancelling a plan only affects the selected workspace and does not affect any other workspaces you belong to.
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
Click **Manage Workspace** for the workspace whose plan you want to cancel.
Click **Cancel Plan**, which will redirect you to Stripe.
If you don't see the Cancel Plan button, the workspace may already be on the Free plan.
If not, please contact support.
Click **Cancel Plan** to confirm.
You'll keep access until the end of your current billing period. After that, the workspace will move to a Free plan.
Once cancellation is scheduled, you won't be able to make changes to the workspace's plan until the cancellation takes effect at the end of the current billing period.
## FAQs
If you change your mind or need to make changes, contact support to remove the pending cancellation first.
See [Deleting a Workspace](/docs/home/account-admin/workspaces/delete-a-workspace).
**Questions?** Contact support.
# Creating an Account
Source: https://rive.app/docs/account-admin/account-overview/creating-an-account
To create a Rive account, go to [rive.app/signup](https://rive.app/signup/?redirect=%2Faccount%2Fteams%2F\&utm_source=docs\&utm_medium=content) and follow the signup steps.
After signing up, you can use Rive for free or upgrade a workspace later.
## FAQs
Yes. You can create a free Rive account and start using the editor without upgrading.
If you previously deleted a Rive account with the same email address, you won't be able to create a new account with that email. Contact support to restore the old account instead.
To collaborate with others, create or join a workspace. See [Creating a Workspace](/docs/account-admin/workspaces/managing-workspaces#creating-a-new-workspace).
Yes. See Rive Student Plan Request for details.
# Delete My Account
Source: https://rive.app/docs/account-admin/account-overview/delete-my-account
Deleting your account is permanent and removes all your data after 90 days.
## Before You Can Delete
You need to remove all workspaces from your account first:
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. For each workspace you own:
* Click "Manage Workspace"
* If it's a paid plan, click "Cancel Plan" first
* Then click "Delete Workspace"
3. Once all workspaces are removed, you can delete your account
## What Happens to Your Data
* Your account and data are deleted 90 days after cancellation
* Files you contributed to other people's workspaces are **not** deleted
* Content others have remixed from your Marketplace uploads is **not** deleted
## If You Own a Workspace With Members
Deleting your account suspends access for everyone in your workspace. If you want the workspace to continue, transfer ownership to another admin before deleting your account. See [Transfer Workspace Ownership](/docs/account-admin/workspaces/managing-workspaces#transferring-workspace-ownership).
If you delete your account and later want to use the same email address, you won't be able to create a new account. Contact support to restore your account instead. Note: we cannot guarantee your files will still be available.
# Downloading My Receipt/Invoice
Source: https://rive.app/docs/account-admin/account-overview/downloading-my-receipt-or-invoice
Invoice links in email expire after about a week. If your link no longer works, you can always download invoices directly from your account.
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. Find the workspace in the left sidebar
3. Click "Manage Workspace"
4. Click "Manage Billing" (opens Stripe)
5. If prompted, enter your email—Stripe will send you a login link
6. Scroll to the bottom to view your invoice history
7. Click any invoice date to open it, then download the PDF
Check your spam folder if the Stripe email doesn't arrive within a few minutes.
**Questions?** Contact support.
# Trouble Logging In
Source: https://rive.app/docs/account-admin/account-overview/trouble-logging-in
## "Invalid credentials" error
Two common causes:
**1. You signed up with Google or Facebook** If you created your account using Google or Facebook login, we don't have a password stored—that's managed by them. Use the Google or Facebook sign-in button instead of email/password.
**2. Wrong password** Click "Forgot password?" below the login button. We'll email you a reset link.
**Questions?** Contact support.
# Bring Your Own S3 Bucket
Source: https://rive.app/docs/account-admin/bring-your-own-bucket
Configure your own AWS S3 bucket to work with Rive
This feature is available exclusively to Enterprise plan customers. If you're interested in upgrading to unlock this capability, [contact us](https://rive.app/enterprise?utm_source=docs\&utm_medium=content).
## What is a Custom S3 Bucket?
A custom S3 bucket allows Enterprise customers to store their Rive file and asset data in their own AWS environment instead of Rive's default storage. This premium feature gives you greater control over your data, allowing you to:
* Apply your organization's security and compliance policies
* Keep data within your existing AWS infrastructure
* Integrate with your existing backup and disaster recovery processes
* Monitor and audit all access to your bucket using CloudTrail
If you have existing data in Rive that you would like migrated to your custom S3 bucket after upgrading to Enterprise, our team can help coordinate the migration process.
## Configuration
Follow the [AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html) to create a new S3 bucket:
* Choose a unique name for your bucket
**Note:** Use that name in place of `BUCKET_NAME` for the rest of this document
* Configure basic bucket settings
* Block public access
* You can choose to leave everything as default or you can decide to enable or customize:
* versioning (disabled by default)
* encryption - by default buckets and new objects are encrypted by Amazon's S3 managed keys (SSE-S3) which uses AES256
* tags
* In the AWS Console, under IAM / Policies click on *"Create policy"*
* Select the *"JSON"* Policy Editor view and paste the following:
```json theme={null}
{
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:AbortMultipartUpload",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
}
],
"Version": "2012-10-17"
}
```
* In the AWS Console, under IAM / Roles click on *"Create role"*
* Under *"Trusted entity type"* pick *"Custom trust policy"*
* In the JSON editor that appears paste the following:
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "REQUEST_FROM_RIVE"
},
"Action": "sts:AssumeRole"
}
]
}
```
**Note:** You will need to request the appropriate value to replace `REQUEST_FROM_RIVE` from your Rive representative
* Hit *"Next"* to go to the *"Add permissions"* section
* Under *"Permission policies"* search for the IAM Policy you just created and select it
* Click *"Next"* to go to *"Name, review, and create"*
* Choose a name, review the trust policy and the permissions, and click *"Create role"*
* Open the Role you just created and make note of the ARN
Share the following information with your Rive representative:
* **Region**: Region of the S3 Bucket
* **Bucket Name**: Name of the Bucket
* **Role ARN**: ARN of the role you created
After providing all the above to your Rive representative, our team will configure your account to use your custom S3 bucket.
You'll receive a confirmation once everything is set up, at which point all your Rive resources will automatically be stored in your own S3 bucket.
## Troubleshooting
If you encounter issues with your S3 bucket configuration:
* Verify the IAM Role has the correct trust relationship (using the value provided by Rive)
* Ensure the IAM Policy has the necessary S3 permissions
* Check that your bucket is in the same region you provided to Rive
* Contact your Rive representative for additional assistance
# Pricing
Source: https://rive.app/docs/account-admin/pricing
For full details and feature comparison, see [rive.app/pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
## Plans
| Plan | Monthly | Annual (paid upfront) | Best for |
| :------------- | :----------- | :-------------------- | :------------------------------------------------------- |
| **Free** | \$0 | \$0 | Learning Rive, personal projects |
| **Cadet** | \$17/seat/mo | \$108/seat/year | Small teams shipping to production (3 seats max) |
| **Voyager** | \$39/seat/mo | \$304/seat/year | Teams needing Libraries and collaboration (25 seats max) |
| **Enterprise** | — | \$1,440/seat/year | Large organizations (\$10M+ annual revenue) |
Annual billing saves money but requires full payment upfront.
## What's Included
**Free**
* 3 collaborative files
* State Machines, Data Binding, Scripting
**Cadet**
* Exports for runtime
* Unlimited files
**Voyager** (everything in Cadet, plus)
* Libraries
* CDN asset hosting
* Embed link hosting
* \$20/seat monthly agent credits
* Priority Community support
**Enterprise** (everything in Voyager, plus)
* Dedicated Slack with Rive team
* SSO and SOC2 Type II
* Custom S3 bucket
* Subteam workspaces
* Org-wide permissions
* Onboarding and training
* Custom runtime support
* Centralized billing
* \$40/seat monthly agent credits
***
## Legacy Pricing
If you're on a legacy plan, your pricing is locked in as long as your plan stays active. Adding users may move you to current pricing. If your plan cancels, legacy pricing cannot be reinstated.
***
## Students
Rive offers a discounted Student Plan for individual students with a valid student email or documentation. The Student Plan is for personal educational work only—not for commercial use or team collaboration. Complete this form to request access to the Student Plan.
Schools, universities, and non-profits should use a standard Cadet or Voyager plan.
***
**Questions?** Contact support.
# Managing Workspace Members
Source: https://rive.app/docs/account-admin/workspaces/managing-workspace-members
Add, remove, and change the roles of members in a workspace.
## Member Roles
| Role | What they can do | Cost |
| :--------- | :----------------------------- | :-------- |
| **Viewer** | View files only | Free |
| **Editor** | View and edit files | Paid seat |
| **Admin** | View, edit, and manage members | Paid seat |
## Inviting Members to a Workspace
Inviting a member to a workspace gives them access to all projects in that workspace.
If you only want to give a member access to a specific project, open that project and invite them from the right sidebar.
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
Click **Manage Workspace** next to the workspace you want to add a member to.
Under **Members**, click **Invite Members**.
Enter the member's email address, select their role, and click **Confirm**.
For paid workspaces, you're charged a prorated amount immediately for each Editor or Admin seat.
## Accepting an Invite to a Workspace
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content) and make sure you're logged in to the correct Rive account.
Follow the invite link in your email.
## Changing Member Roles
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
Click **Manage Workspace** next to the workspace you want to update.
Under **Members**, click **Manage Roles**.
Select the new role and click **Confirm**.
For paid workspaces, you're charged a prorated amount immediately for each Editor or Admin seat.
## Removing Members from a Workspace
Admins can remove members from a workspace at any time.
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
Click **Manage Workspace** next to the workspace you want to update.
Under **Members**, click the **x** next to the member you want to remove.
The workspace owner can't be removed. If you need to change ownership, contact support.
## FAQs
Make sure your email address is verified. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content), open the **Profile** tab, and click **Resend verification email** if needed.
Rive charges per seat per workspace. If you're an Editor in multiple workspaces, each workspace pays for your seat separately. To collaborate, one of you should invite the other to their workspace, and that workspace pays for the seat.
Check your spam folder and make sure the invite was sent to the correct email address.
# Managing Workspaces
Source: https://rive.app/docs/account-admin/workspaces/managing-workspaces
Create, upgrade, transfer, and delete workspaces.
## Workspace Admin
The workspace admin lets you:
* View your current AI credit usage and purchase more
* View and invite members to the workspace
* View projects in your workspace
To view the workspace admin, go to the editor **Home** tab, open the dropdown next to the workspace name, and select **Admin**.
## Creating a New Workspace
Create a new workspace when you want a separate space for a team or project.
Workspaces are separate spaces for files, members, and billing. If you only want to organize files or share a specific set of files, create a new project instead.
From the Editor home screen, open the workspace dropdown and select **+ New Workspace**.
Choose a workspace name, invite members, and click **Create**.
You can add or remove members from Workspace Settings later.
You should now see an empty workspace with a **Personal** project. You can create files in this workspace.
## Upgrading a Workspace
For more information about plans and features, see [Pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
Each workspace is billed separately. Make sure you upgrade the workspace where you want to use the paid features.
Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content).
Click **Manage Workspace** next to the workspace you want to upgrade.
Click **Upgrade Plan**.
Select a plan and billing option.
Follow the checkout steps to complete your payment.
## Transferring Workspace Ownership
Make sure the new owner is already a member of the workspace and has the Admin role. See [Inviting Workspace Members](/docs/account-admin/workspaces/managing-workspace-members#inviting-members-to-a-workspace) and [Changing Member Roles](/docs/account-admin/workspaces/managing-workspace-members#changing-member-roles).
Contact support with:
* The workspace name
* The email address of the current owner
* The email address of the new owner
We'll handle the transfer for you.
## Deleting a Workspace
If you want to cancel your plan without permanently deleting your files, see [Cancel Your Plan](/docs/account-admin/account-overview/cancel-my-account).
To permanently delete a workspace for all members, including all files in the workspace, see [Deleting a Workspace](/docs/home/account-admin/workspaces/delete-a-workspace).
## FAQs
You probably upgraded a different workspace. Use the workspace switcher to find your paid workspace.
Each workspace is billed separately. Upgrading one workspace doesn't affect the others.
If you had legacy pricing, it was locked in as long as your plan stayed active. Once canceled, we can't reinstate it. You'll need to upgrade at the current price.
# Reactivating a Canceled Plan
Source: https://rive.app/docs/account-admin/workspaces/reactivating-a-canceled-workspace
If your workspace was canceled but not deleted, you can reactivate it.
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. Find the canceled workspace in the left sidebar (status shows "Suspended")
3. Click "Manage Workspace"
4. Click "Reactivate Plan"
5. Select monthly or yearly billing
6. Click "Confirm & Pay"
Your workspace is now active again.
***
## ⚠️ Don't Create a New Workspace by Mistake
"Create new Workspace" is **not** the same as reactivating. If you have multiple workspaces, double-check you're reactivating the right one—each workspace has a display name and a username.
**If you accidentally created a new workspace instead of reactivating:** Contact support. We can cancel the incorrect workspace and refund you, but we can't transfer payments or files between workspaces. If you saved files in the wrong workspace, download your backups (.rev files) before we cancel it.
**Questions?** Contact support.
# Workspaces
Source: https://rive.app/docs/account-admin/workspaces/workspaces-overview
Workspaces are separate areas in Rive for organizing files and collaborating with others.
**Each workspace is isolated from the others. It has its own projects, files, members, roles, permissions, AI credits, and billing plan.**
For example, you might have:
* A personal workspace for your own files
* A company workspace for your team’s files
* A client's workspace that you've been invited to
Workspaces are separate spaces for files, members, and billing. If you only want to organize files or share a specific set of files, create a new project instead.
## Switching Workspaces
To view or work with files in another workspace, go to the editor Home tab, click the dropdown next to the workspace name, and hover over Switch Workspace.
## FAQs
Rive files live in the workspace they were created in or shared with. Try looking for it in a [different workspace](/docs/account-admin/workspaces/workspaces-overview#switching-workspaces).
# Community
Source: https://rive.app/docs/community/community-overview
# Marketplace
Source: https://rive.app/docs/community/marketplace
# Marketplace Overview
Source: https://rive.app/docs/community/marketplace-overview
The [Rive Marketplace](https://rive.app/marketplace?utm_source=docs\&utm_medium=content) is a place where creators can share their files, get feedback, and learn by opening others’ files directly in the Rive editor.
Marketplace files are all shared under a [CC BY](https://creativecommons.org/licenses/by/4.0/) license.
## Remixing
A Remix is a copy of another user's file. It uses their file as a starting point for a new file in your account. Remixing encourages sharing, and you'll probably learn some new techniques too!
To Remix a file from the Marketplace, find one you like, click on the thumbnail, and use the remix button on the right side.
The file will open in the editor, and a new copy is added to your file browser.
# Rive Experts
Source: https://rive.app/docs/community/rive-experts
# Support
Source: https://rive.app/docs/community/support
Choose the best way to get help with Rive—from community support to direct help from our team.
## Quick Links
Not sure where to start? Ask in the [community](https://community.rive.app/c/support?utm_source=docs\&utm_medium=support_page\&utm_content=peer_support) — you’ll usually get the fastest response.
### Get Help
* [Peer-to-peer Support](https://community.rive.app/c/support?utm_source=docs\&utm_medium=support_page\&utm_content=peer_support) — Best for how-to questions, learning Rive, and general debugging help from the community
* [Voyager Support](https://community.rive.app/c/voyager-support?utm_source=docs\&utm_medium=support_page\&utm_content=voyager_support) — Priority support with direct help from the Rive team for production issues and blocking bugs (included with Voyager)
* [Enterprise](https://rive.app/pricing?utm_source=docs\&utm_medium=support_page\&utm_content=enterprise_support) - For teams that need closer collaboration, Enterprise plans include a dedicated Slack channel with the Rive team
* [Discord](https://discord.com/invite/FGjmaTr) — Chat with the community in real time for quick questions and discussion
* Account support — Billing, account issues, or private inquiries
For faster, more accurate help, include a `.rev` file, a video, a code snippet, and a minimal example.
### Report & Request
* [Report a Bug](https://community.rive.app/c/bug-reports?utm_source=docs\&utm_medium=support_page\&utm_content=bug_report) — Report issues with the editor or runtimes
* [Request a Feature](https://community.rive.app/c/feature-requests?utm_source=docs\&utm_medium=support_page) — Suggest improvements or new features
* [Early Access](https://community.rive.app/c/early-access?utm_source=docs\&utm_medium=support_page) — Discuss and give feedback on early features
***
## FAQs
Yes. See Rive Student Plan Request for details.
For most technical questions, the fastest way to get help is through the community.
You’ll often get answers from both the Rive team and other experienced users.
If you're working on something production-critical or need direct help from the Rive team, consider [Voyager support](https://rive.app/pricing).
Use email support for:
* Billing or account issues
* Security concerns
* Private or sensitive information
For general how-to questions or debugging help, the community is a better place to start.
Voyager support gives you direct access to the Rive team with faster, more in-depth help.
This is best for:
* Production issues
* Blocking bugs
* Help debugging complex setups
[Upgrade to Voyager](https://rive.app/pricing?utm_source=docs\&utm_medium=support_page).
See [Cancelling My Plan](/docs/account-admin/account-overview/cancel-my-account).
You can browse runtime-specific FAQ pages here:
* [Apple Runtime FAQ](/docs/runtimes/apple/faq)
* [Flutter Runtime FAQ](/docs/runtimes/flutter/faq)
* [Web Runtime FAQ](/docs/runtimes/web/faq)
* [Choose a Renderer FAQ](/docs/runtimes/choose-a-renderer/faq)
# Reduced Motion
Source: https://rive.app/docs/editor/accessibility/reduced-motion
Make Rive animations more comfortable for users who prefer reduced motion.
Reduced motion preferences let users request less non-essential motion in apps and operating systems.
Rive can’t automatically decide which motion should be reduced because motion may be decorative, functional, or part of the meaning of an interaction. To support reduced motion, pass the user’s motion preference into your Rive file with a data binding property, such as `prefersReducedMotion`. Then use that property to decide how your state machine, timelines, and bound properties should behave.
Reduced motion preferences are usually detected by the app or platform, then passed into the Rive file at runtime. For web, this often starts with the `prefers-reduced-motion` media feature. See [MDN’s `prefers-reduced-motion` documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/%40media/prefers-reduced-motion) for accessibility guidance.
## Common Strategies
### Use a Separate Reduced-Motion Path
For complex interactions and decorative motion, create separate state machine paths for full motion and reduced motion. Use a `prefersReducedMotion` Boolean property to choose which path runs.
### Reduce or Disable Animation Speed
For simpler cases, bind transition durations, timeline speeds, or animation speeds to a property that changes when reduced motion is enabled.
For example, full motion might use a normal speed value, while reduced motion uses `0` or a lower value.
Use [Converters](/docs/editor/data-binding/converters) to convert your `prefersReducedMotion` boolean to a value of 0.
Create a new converter group called `BooleanToSpeed`.
This will toggle the boolean so that when `prefersReducedMotion` is `true`, the output will be `false`.
This transforms a `boolean` to a number where `true = 1` and `false = 0`.
With the nested component selected, data bind the **Speed** value to `prefersReducedMotion` and add the `BooleanToSpeed` converter.
Now when `prefersReducedMotion` is `true`, the animation speed will be 0.
### Replace Motion With Non-Motion Feedback
Reduced motion does not always mean removing feedback. Instead of movement, scale, rotation, or bounce, use changes in opacity, or color.
### Reduce Distance
Large spatial movement can be difficult even when it is slow. Consider reducing the distance an object travels, or replacing large movement with a small offset and fade.
## Runtime Setup
At runtime, use [data binding](/docs/runtimes/data-binding) to pass the user’s motion preference into the Rive file.
In the future, runtimes may expose a built-in reduced motion value that can be read directly by the `.riv` file. For now, pass the user’s motion preference into the file yourself.
The Rive file should use that property to control state machine transitions, timeline speeds, or bound animation properties.
Rive does not automatically apply reduced motion. File authors need to decide which motion is essential, which motion is decorative, and what the reduced-motion experience should be.
# Semantics
Source: https://rive.app/docs/editor/accessibility/semantics
Make your experiences accessible to screen readers by defining semantic types, properties, states, and actions
Semantics is currently only available in [Early Access](https://rive.app/downloads?utm_source=docs\&utm_medium=content). Runtime support is in development or released as experimental.
Have feedback? Join the [Early Access community](https://community.rive.app/c/early-access/) to share your thoughts and help shape the feature.
Semantics describe what an element is and how it should be understood. Adding semantics makes your experiences accessible, allowing screen readers to correctly describe and navigate your content.
**Architecture: Role / Trait / State / Actions**
* [Role](#semantic-role) — what the node is (button, checkbox, tab, etc.)
* [Properties](#semantic-properties) — additional information like label, value, or hint
* [Traits](#traits) — what the node can do (expandable, selectable, checkable, etc.)
* [State](#state) — what the node is doing (expanded, selected, checked, etc.)
* [Actions](#actions) - interactions triggered by the user (tap, increase, decrease)
**Semantics at Runtime**
At runtime, semantics are converted into the platform’s accessibility system.
On web, this creates accessible DOM elements (for example, divs with roles and attributes). On iOS and Android, a semantic tree is generated and exposed to the system’s accessibility APIs.
Rive handles mapping your semantics to each platform automatically. For details on which runtimes currently support semantics, see [Feature Support](/docs/feature-support).
## Adding Semantics
Select an element in your scene. In the right sidebar, click the `+` button in the **Semantics** panel to enable semantics for that element.
Use the dropdown to assign a [semantic role](#semantic-role), such as button, image, or heading.
Update the element’s [semantic properties](#semantic-properties), [Traits](#traits), and [State](#state).
### Semantic Role
The semantic role defines what the element *is* (for example, a button or image). Screen readers use this to describe the element and determine how it should behave.
Each role has different available [properties](#semantic-properties).
If your element doesn’t match a specific role, you can leave it set to **None**.
Some elements, like text, automatically infer their semantic role. See [Semantic Inference](#semantic-inference) for more info.
## Semantic Properties
Semantic properties provide additional information about an element, such as its label, value, or hint.
Available properties vary depending on the selected semantic role.
### Label
The label describes what the element is or does. This is what screen readers announce.
For example, a slider that controls volume might have the label:
`Volume`
Text elements automatically use their text content as the label. See [Semantic Inference](#semantic-inference) for more info.
### Heading (Text only)
Text elements can be marked as headings to define structure and hierarchy.
Options include **None** and **Heading 1** through **Heading 6**.
Headings help screen reader users navigate your content more easily.
On web, Heading 1 maps to an `
` element, Heading 2 to `
`, and so on.
### Value (Inputs only)
Some elements, like sliders and switches, have a **Value** property that reflects their current state.
For example, a volume slider might have a value of `70%`.
Use data binding to keep the semantic value in sync with the visual state.
***
## Traits
Traits define what an element *can do*, while states describe what it is doing right now.
Some semantic roles include traits, such as **expandable** or **selectable**. Enabling a trait makes its corresponding [state](#state) available.
The lock icon indicates a required trait for that semantic role. For example, a checkbox always includes the **selectable** trait.
***
## State
States represent the current value of the node.
For example, an element with the **selectable** trait will have a **selected** state.
Some state flags are only meaningful when their corresponding trait is set. Without the trait, the platform sees the property as not applicable.
Keep semantic states in sync with your Rive file. For example, bind a boolean from your view model to the **toggled** state of a switch. You could also key the **toggled** state in a timeline.
### Hidden
Hides the element from the accessibility tree.
Elements that are not visible (for example, hidden by layout or soloing) are automatically removed from the accessibility tree and do not require this property.
Use **Hidden** when an element is still present in the scene (for example, opacity is set to 0) but should not be exposed to assistive technologies.
### Disabled
Marks the element as disabled and not interactive.
### Live Region
Indicates that the element’s content may update dynamically and should be announced by screen readers.
## Actions
Semantic actions are interaction events triggered by assistive technologies. You can listen for these actions and update your experience in response.
### Adding an Action
Select your object and [create a new Listener](/docs/editor/state-machine/listeners).
With your listener selected, in the right sidebar choose **Semantic Action** in the **Listen to** dropdown.
Select the [action type](#types-of-actions) to listen for.
### Types of Actions
Actions include **tap**, **increase**, and **decrease**. Additional actions may be added in future releases.
#### Tap
Represents activating an element (similar to a click).
When a user activates an element using a screen reader, a **tap** action is triggered. You should listen for this action and handle it the same way as a standard click or pointer interaction.
It’s common to add both a **Click** and a **Semantic Tap** listener to the same element to support pointer and assistive interactions.
#### Increase / Decrease
Used for adjustable elements like sliders.
Users trigger these actions through assistive technologies—for example, by focusing a slider and swiping up or down with a screen reader, or using arrow keys on a keyboard.
You should listen for **increase** and **decrease** and update your value accordingly.
### Semantic Inference
Some roles and properties are inferred based on the element they are applied to.
For example, when adding semantics to a text element, the [Role](#semantic-role) is set to **Text**, and the [Value](#value) is automatically derived from the element’s text content.
***
## Testing Semantics
Adding semantics is only the first step. You should always test your experience at runtime to make sure it behaves as expected.
Small issues—like a missing label or incorrect state—can make elements confusing or unusable for screen reader users.
### Runtime Testing
The best way to test semantics is by using a screen reader.
* **macOS / iOS**: VoiceOver
* **Android**: TalkBack
* **Windows**: Narrator or NVDA
Turn on a screen reader and navigate through your experience:
* Move between elements
* Listen to how each element is announced
* Interact with buttons, sliders, and inputs
### What to Check
As you test, pay attention to:
* **Labels** — Does each element clearly describe what it is or does?
* **Role** — Is the correct role announced (button, heading, etc.)?
* **Value** — Do dynamic elements (like sliders) report the correct value?
* **State** — Are states like selected, disabled, or expanded accurate?
* **Order** — Does navigation follow a logical flow?
# AI Agent
Source: https://rive.app/docs/editor/ai-agent/ai-agent
Rive's AI agent helps you write code, design, and animate.
The AI Agent is available with Cadet, Voyager, and Enterprise plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
Rive's AI agent helps you write code, design, and animate. Generate scripts, responsive layouts, data models, and even animation.
## Getting Started
Open the **Agent panel** from the left sidebar and type a prompt describing what you want.
**Example prompt:**
> Create a script that draws a rounded rectangle.
> Width, height, color, and corner radius should be inputs.
> When the inputs change, the drawing should update.
The Agent will respond with:
* A summary of what it changed
* Any new scripts it created
* Explanations for how the script works
* Suggestions for how to customize it
If a new script is generated, you’ll find it in your Assets panel.
## Toolbar
Use the AI Agent toolbar to manage your conversations:
* **New Chat** – Start a new chat thread without losing your past conversations
* **Toggle Chats** – Go back to old chats
* **Close Chat** – Close the chat without losing your history
## FAQ
The AI Coding Agent is billed using AI credits, which are expressed in USD.
$1 in AI credits = $1 USD of AI usage
**What's included per paid seat (monthly AI credits)?**
Each paid seat adds monthly AI credits to your workspace:
* Voyager: \$20/seat/month in AI credits
* Enterprise: \$40/seat/month in AI credits
**AI credit rules**
* Monthly AI credits reset each month and don’t roll over.
* AI credits are shared at the workspace level.
* Monthly credits are always consumed before top-up credits.
* AI credit top-ups don't expire
When your workspace runs out of available AI credits, the agent stops accepting new prompts until:
* Your monthly AI credits reset, or
* You add an AI credit top-up
No. We have a Zero Data Retention policy active with our LLM provider. Neither we nor the provider use user data to train AI models.
Admins on Enterprise workspaces can toggle the AI Coding Agent on or off for the entire workspace at any time.
Yes. Like any AI system, the agent may occasionally generate incorrect or suboptimal code, but it will attempt to self-correct. Always review code generated by the AI Coding Agent.
Yes. Please send feedback to our [Community](https://community.rive.app/).
# Rive MCP Integration
Source: https://rive.app/docs/editor/ai/mcp
MCP integration is currently available only in the desktop Editor for Windows and macOS.
You can connect the Rive Editor to AI tools through MCP (Model Context Protocol). The first set of tools are designed to let AI handle repetitive tasks, like creating complex View Models, State Machines with hundreds of states/layers, Layouts, Shapes, and more.
### Supported Features
* **Create and manage Rive files** — add, rename, resize, arrange, and focus artboards.
* **Inspect and edit the scene** — query hierarchies, select objects, update properties, rename, duplicate, reorder, reparent, or delete elements.
* **Build designs** — create shapes, paths, layouts, component instances, component lists, and asset-based elements.
* **Create animation and interaction** — edit linear animations, state machines, states, transitions, conditions, keyframes, and interpolation.
* **Work with data binding** — create view models, properties, instances, bindings, and custom property groups.
* **Edit scripts and shaders** — manage Luau scripts and WGSL shaders, update source code, run diagnostics, recompile, test, search code, and read console output.
This list of features will evolve over time as more tools are added.
## Installation
### Using Cursor
Install and open the latest version of the [Rive](https://rive.app/downloads) desktop app for Mac or Windows.
Create a [Cursor](https://www.cursor.com/) account and install the app.
Open Cursor and navigate to the settings panel in the top right corner.
Open the Tools & MCPs tab and click Add Custom MCP.
Save the following JSON snippet to your computer as `mcp.json`.
```json theme={null}
{
"mcpServers": {
"rive": {
"url": "http://127.0.0.1:9791/mcp"
}
}
}
```
Turn the MCP connection `On`
If everything was installed correctly, you should see Rive as an available MCP server.
For the Rive server to be available, you must have the Rive Early Access app opened.
Additional information on setting up MCP can be found [here](https://docs.cursor.com/context/model-context-protocol).
### Using Claude in Terminal
Install and open the latest version of the [Rive desktop app](https://rive.app/downloads) for macOS or Windows.
Run the following command in your terminal:
```shell theme={null}
claude mcp add --transport http rive http://127.0.0.1:9791/mcp
```
Run Claude and follow the prompts:
```shell theme={null}
claude
```
## What can it do?
Once Cursor is installed and everything is set up correctly, it's time to start prompting the AI.
Have a Rive File open and an Artboard created.
Type your prompt into the chat UI and hit enter. The AI will take a moment to process the request.
Example prompt:
```
Create a State Machine about birds with 20 states and 2 layers
```
Once the request has been processed, type **End Prompt** to allow the AI to make changes to the Rive file.
# Animate Mode Overview
Source: https://rive.app/docs/editor/animate-mode/animate-mode-overview
Use [animate mode](/docs/editor/fundamentals/design-vs-animate-mode) to create timelines, state machines, keys, transitions, and interactive animation logic.
In Animate Mode, the editor shows animation tools such as the timeline, animations list, key controls, and interpolation options.
Editing objects while a timeline is selected can create keys. See [Design and Animate Modes](/docs/editor/fundamentals/design-vs-animate-mode) to learn how to avoid accidental keying.
### Learn more about Animate mode
} href="/editor/animate-mode/timeline">
The Rive interface displays a timeline with playback controls and options for the current animation in Animate mode.
} href="/editor/animate-mode/keys">
Objects and their properties appear on the Timeline once they have been keyed. There are a few different ways to key a property.
} href="/editor/animate-mode/interpolation-easing">
When you set two keys on a property, the value in between those keys is automatically calculated. This is called interpolation.
} href="/editor/animate-mode/animating-draw-order">
You can change the draw order of your graphics at design time by moving items up and down the Hierarchy.
## Use case: Building a Bouncing Ball
This video walks you through animating a bouncing ball in Rive. We cover keyframes, interpolation and a couple classic principles of animation.
# Animating Draw Order
Source: https://rive.app/docs/editor/animate-mode/animating-draw-order
You can change the draw order of your graphics at design time by moving items up and down the [Hierarchy](/docs/editor/interface-overview/hierarchy)– but what if you want to change the draw order during an animation? Or what if you want to change the draw order without breaking the current hierarchical structure? Rive allows you to accomplish this with Draw Order Rules.
## Draw Order Rules
To animate the draw order of a group or shape, start by selecting it. Use the Draw Order section of the Inspector to create Draw Order Rules.
The Normal rule is the default order (based on [Hierarchy](/docs/editor/interface-overview/hierarchy) order). When the radio button next to this rule is active, the shape appears at its default draw order.
Draw Order Rules allow you to select a target (note that this must be a drawable item, not a group) and whether to draw above or below the target.
The target must be a drawable item, like a shape. It cannot be a group.
In Animate mode, use the radio button next to the Draw Order Rules to set a key. Note that these are [Hold keys](/docs/editor/animate-mode/interpolation-easing#hold) as Draw Order cannot be interpolated.

# Interpolation (Easing)
Source: https://rive.app/docs/editor/animate-mode/interpolation-easing
When you set two keys on a property, the value in between those keys is automatically calculated. This is called interpolation. Interpolation settings can be customized to create dramatically different results.
You can set the easing on your keys by either using the Interpolation panel to the right of the Timeline, or by using the Graph Editor, which you can toggle on via the shortcut near the Timeline options.
## Using the Interpolation Panel
The Interpolation Panel appears to the right of the timeline when you select any number of keys on the timeline.
The interpolation graph is a visual representation of how the value will change over time from the selected key to the next with the x-axis representing time and the y-axis representing the change in the chosen property.
You can choose which interpolation type to use by selecting any of the icons above the graph.
### Linear

Linear is the default interpolation type, and it creates a constant rate of change from one key value to the next.
### Cubic

Cubic interpolation uses a curve to interpolate between key values. It gives you two handles that can be dragged to customize the curve.
You can drag the handles as far as you want on the Y-axis. If you drag the handles outside of the graph, the graph view will update to ensure that the handles are always in view.
The default cubic curve creates a gentle curve from the first key to the next, which results in the value changing slowly at the start and end, and changing the most in the middle.
### Hold

Hold doesn't interpolate values between keys. It simply holds the current value until the next key is reached, where the next value is set instantly.
### Interpolation field
A text field below the preview graph represents the interpolation in a numerical format. A total of four values (typically between 0 and 1) represent the position of the handles – two for the inward curve, and two for the outward curve. You can see how these values change by dragging the handles within the preview window.
Use this field if you wish to set specific easing values, perhaps defined in a design language for a specific brand, for instance. The field also makes it easy to copy and paste values across files and tools.
When inputting values manually, use a comma or a space to separate each of the four values.

## Default Interpolation
You can set a default interpolation to control the interpolation used by new keys.
There are two ways to set the default interpolation:
### Set a Selected Key as the Default
Select a key, then open the interpolation options in the Inspector and click Set as default.
### Set the Default from the Inspector
Deselect everything on the stage. The Inspector shows the default interpolation options for new keys.
Choose the interpolation you want new keys to use by default.
## The Graph Editor
Rive's Graph Editor visually represents how an object's properties change over time. Using this graph, we can edit the rate of change and the values being interpolated.
### Enabling the Graph Editor
Use the Graph Editor button on the timeline to enable the Graph Editor. You'll notice that the Graph Editor replaces the timeline.
Note that only objects that are selected will appear on the Graph Editor.
### Using the Graph Editor
The Graph Editor gives a visual representation of the current interpolation. You have two ways to edit the interpolation on the graph.
#### Using Cubic interpolation
Cubic interpolation describes the easing function applied between one key and the next, much like a cubic Bezier easing curve in CSS. It shapes how fast the value changes over time, but it always lands exactly on the values you keyed. Adjust a Cubic curve from the Interpolation Panel; the Graph Editor shows the curve, but you set it from the panel.
#### Using Cubic Value
Cubic Value gives you draggable Bezier handles directly on the Graph Editor, similar to After Effects. Because Rive stores the X and Y position of each handle, it uses a little more data than Cubic, but it gives you much more control over the motion, including the ability to overshoot a value before settling on the final key.
That overshoot is the key difference. Cubic interpolation can only move between the values you key, so animating a property from 0 to 0 produces no motion at all. Cubic Value lets you pull the handles so the curve travels past 0 and back, creating real movement even when the start and end values are identical. This makes Cubic Value ideal for bounces, anticipation, and other effects that need to push beyond the final value.
# Keys
Source: https://rive.app/docs/editor/animate-mode/keys
### Set Keys
Objects and their properties appear on the Timeline once they have been keyed. There are a few different ways to key a property. You can manipulate an object directly on the Stage (which will set keys for any resulting transformation, like position, rotation, or scale) or change the property in the Inspector. You can also use the key button that appears next to any property that can be animated. This will set a key for the current value.
Note that the key button has a grey stroke when a key is not set. If the property has been animated, then the property has a blue stroke. If the property has been animated at the current playhead position, then the key button has a blue fill.
### Manipulate keys
Keyed objects and their properties appear on rows in the timeline. Keys for properties are shown in a blue fill. If a property (or multiple properties) is collapsed, then the key appears filled in grey.

You can change a key's position on the timeline by clicking and dragging it to the desired location. Use the grey keys to move all of the keyed properties of an object or use the blue keys to change a single property's location.
## Binding Key Values
You can [data bind](/docs/editor/data-binding/overview) the value stored in a key. This lets a keyed value come from data instead of being fixed directly on the timeline.
Right-click the key you want to bind.
Select **Data Bind**.
Choose the property you want to bind to the key value.
Once bound, the key uses the bound value when the animation reaches that point in the timeline.
### Copy Keys
You can copy the keys from one object to another by copying the property keys from the animated object and pasting them to another object.
## Resize Keys
You can resize a selection of keys by holding `alt` and dragging them from the start or end. This serves as a quick way to shrink or lengthen a selection of keys.

# Timeline
Source: https://rive.app/docs/editor/animate-mode/timeline
The Rive interface displays a timeline with playback controls and options for the current animation in Animate mode. A list of all animations is displayed to the left of the Timeline. Keep in mind that these are animations for the currently [active artboard](/docs/editor/fundamentals/artboards#active-artboard).
## Animation type
**One-Shot:** The playhead stops at the end of the animation.
**Ping-Pong:** The playhead continuously plays from the start point to the end point and back from the end point to the start point.
**Loop:** The playhead continuously loops the animation from the start point to the end point.
**Work Area:** The work area defines the playback area of an animation. Work areas are an easy way to focus on a small portion of a larger animation.
## Navigate Timeline
The Navigate Timeline menu gives you a list of helpful shortcuts to navigate the Timeline.
## Show Only Selected
The Show Only Selected toggle can be helpful when dealing with animations with many different objects keyed. When this option is toggled on, only objects that you have selected will appear on the timeline.
## Timeline Options
Located to the right of the playback select, the Timeline Options show you the current time, duration, playback speed, and snap keys options.
**Current:** The current position of the playhead.
**Duration:** The total length of the timeline.
**Playback Speed:** The speed at which the animation should play. The default speed is 1x. Negative values cause the animation to play backward.
**Snap Keys:** The interval at which keys can be placed on the Timeline. By default, keys can be set at a subdivision of 60 for each second.
## Navigating the Timeline
**Scroll and Zoom:** Use the horizontal scrollbar at the top of the Timeline (with two grabbers on each side) to scroll and resize the Timeline's zoom level.
**Pan:** You can also use the same pan shortcuts as the stage to pan the Timeline (right-click and drag, or hold the spacebar and drag).
# Audio Assets
Source: https://rive.app/docs/editor/assets/audio
Import, clip, and configure audio assets in Rive.
## Importing Audio
See [Importing Assets](/docs/editor/fundamentals/assets-overview#importing-assets) for information about importing files, including MP3, WAV, and FLAC files.
### Browse Sounds
Browse Sounds gives you direct access to a free library of over 3,000 sounds from Soundly.
In the Assets panel, click **+** and select **Browse Sounds**.
In the Sounds panel, search or browse for a sound.
Preview the sound, then click **+** to add it to the Assets panel.
After adding a sound to the Assets panel, you can use it in an [Audio Event](/docs/editor/events/audio-events) or reference it from a [script](/docs/scripting/getting-started).
## Export Options
Audio assets include a **Source** option that controls the audio format used for export.
* **Source**: Use the original imported audio format.
* **WAV**: Export the audio as WAV.
* **FLAC**: Export the audio as FLAC.
* **MP3**: Export the audio as MP3.
## Previewing Sounds
You can create a shorter clip from any audio asset.
Open the **Audio** panel in the **Debug Panel**.
Select an audio asset in the **Assets** panel.
Click the play icon.
## Creating Clips
You can create a shorter clip from any audio asset.
Open the **Audio** panel in the **Debug Panel**.
Select an audio asset in the **Assets** panel.
Drag along the waveform to select a portion of the audio and click the play button to preview.
Click **+** to create a clip.
The new clip will now appear as its own audio asset under the parent audio in the sidebar.
## Setting Asset Volume
Select an audio asset or clip to set its **Volume** in the Inspector.
Volume is set on the asset, not on each individual use of the asset. If the same audio asset is used in multiple places, changing its volume affects all of them.
# Photoshop Files
Source: https://rive.app/docs/editor/assets/psd
Using layered Photoshop files in Rive.
## Importing PSD Files
Drag a PSD file into Rive or import it from the Assets panel. When you import a PSD file, Rive adds its visible image layers to the Assets panel so you can place individual layers or the full composition on an artboard.
Hidden layers in the PSD are not imported into Rive.
## Using PSD Layers
You can use PSD layers individually or as a full composition.
* Drag a layer onto an artboard to place that layer.
* Drag the entire PSD onto an artboard to create a group containing all imported layers.
## Asset Settings
PSD layers use the same asset settings as raster images. You can configure settings for individual layers or apply settings to all layers in the PSD.
## Export Behavior
Only PSD layers used in the Rive file are included in the exported `.riv` file by default.
You can adjust export options for individual layers or for all layers in the PSD. See [Assets](/docs/editor/fundamentals/assets-overview#asset-settings) for more about export behavior.
## Reimporting PSD Files
You can replace or reimport a PSD to update its layers in Rive.
Keep PSD layer names consistent when reimporting. If you rename a layer in Photoshop and reimport the PSD, Rive treats it as a new layer. Any objects using the old layer may lose their asset reference.
# SVG & Vector Assets
Source: https://rive.app/docs/editor/assets/svg
Import SVG files and convert them into editable vector artwork.
SVG is one of the easiest ways to bring existing vector artwork into Rive.
When you import an SVG, Rive converts the SVG into native Rive vector objects. After import, the artwork is no longer an SVG—it becomes editable shapes, paths, fills, strokes, and groups that behave like any artwork created directly in Rive.
## Importing SVG Files
You can import SVG files or copy vector artwork from design tools like Figma and Illustrator.
For general importing steps, see [Assets overview](/docs/editor/fundamentals/assets-overview).
### Importing from Figma
You can use **Copy as SVG** and paste it directly into the Rive editor.

### Importing from Illustrator
When exporting or copying SVGs from Illustrator, use **Presentation Attributes** for styling.
* If you use **Save As**, open **SVG Options** and set **CSS Properties** to **Presentation Attributes**.
* If you use **Export As**, open **SVG Options** and set **Styling** to **Presentation Attributes**.
* If you copy and paste directly from Illustrator into Rive, Illustrator uses the **Export As SVG** options, so make sure Styling is set to **Presentation Attributes** there.
Also disable **Preserve Illustrator Editing Capabilities**. This adds extra Illustrator data that can make the SVG much larger and may include data Rive’s importer does not recognize.
## Export Behavior
SVG assets only use **Automatic** export behavior.
Rive converts SVGs into native vector objects before runtime export. The original SVG file is not included in the `.riv`, and runtimes do not currently convert SVG assets at runtime.
## Unsupported Features
Most SVG artwork imports without issue, but some SVG features do not have direct equivalents in Rive and are imported differently or ignored.
| SVG Feature | Import Behavior |
| --------------------------------------- | --------------------------------------------------------- |
| Embedded images | Ignored. |
| Gradient transforms | Ignored. Linear and radial gradients are still supported. |
| `stroke-dasharray` | Imported as a solid stroke. |
| `mask` | Imported as clipping. |
| `filter` | Not supported. |
| `skew` transforms | Not supported. |
| Inherited fills and strokes (`inherit`) | Inherited colors default to white. |
If your artwork relies on unsupported SVG features, consider converting them to editable paths in your design tool before importing into Rive.
# Constraints Overview
Source: https://rive.app/docs/editor/constraints/constraints-overview
Learn how to use constraints in Rive.
Constraints are a way to control the properties of an object through another target object. Some constraints can set limits on these properties (and their hierarchical relationships), while others can copy properties from one object to another.
## Learn By Example
## Common Use Cases
* Make a character's eyes follow a target.

* Ensure a character's feet stay planted on the floor while their legs automatically bend at the knees.

* Make all the wheels on a vehicle rotate together.
* Make the hands on a clock rotate.
* Copy translation, rotation, or scale from another object.
* Push an object away as one gets closer, or ensure an object always stays close to another one.
Types of constraints in Rive:
* [IK Constraint](/docs/editor/constraints/ik-constraint)
* [Distance Constraint](/docs/editor/constraints/distance-constraint)
* [Transform Constraint](/docs/editor/constraints/transform-constraint)
* [Translation Constraint](/docs/editor/constraints/translation-constraint)
* [Scale Constraint](/docs/editor/constraints/scale-constraint)
* [Rotation Constraint](/docs/editor/constraints/rotation-constraint)
# Distance Constraint
Source: https://rive.app/docs/editor/constraints/distance-constraint
The Distance Constraint makes an object stay close, far, or exactly at a specific distance to another object.
## How to create a Distance Constraint
Use the Constraints section of the Inspector to add a Distance Constraint to an object.

Use the new constraint's fly-out menu to select a target for this constraint.

Moving the target object now causes the constrained object to stay close (which is the default mode).

## Strength
The Strength property determines how much the constrained object is affected.
A Strength of 0% means the constraint won't have any effect.
## Distance
The distance that the object will be constrained from the target object. A red constraining circle is drawn on the stage to represent this value.
## Mode
### Closer
The owner is constrained closer than the Distance setting. In other words, the owner is constrained inside the constraining sphere.
### Further
The owner is constrained further than the Distance setting. In other words, the owner is constrained outside the constraining sphere.
### Exactly
The owner is constrained exactly at the distance of the constraining sphere.
# Follow Path Constraint
Source: https://rive.app/docs/editor/constraints/follow-path-constraint
The Follow Path Constraint makes complex motion much easier to create by allowing us to constrain an object to a path. Watch the video, or read more below.
For text that follows a path, use the [Follow Path Text Modifier](/docs/editor/text/text-modifiers#follow-path) property instead.
## Setting up a Follow Path Constraint
First we’ll need both an object to constrain and a path to constrain it to.
Next, add a new constraint and select the Follow Path Constraint.

Now, use the target button and select the path you want to constrain the object to.
## Follow Path Properties
Like other Constraints, the Follow Path Constraint has many different properties we can customize.
#### Strength
The strength property dictates how strictly the constrained object will adhere to the constrained property.
#### Target
The Target tells the constraint which path to follow.
#### Distance
The Distance property moves the object up and down the path. As the percent increases, the constrained object moves along the path. Note that this property can exceed 100%.

#### Orient
The Orient toggle controls the constrained objects rotation.
When the Orient toggle is on, the object will adjust its rotation according to the path. Note that you can’t make manual rotation changes to the object in this state.

When the Orient toggle is set to off, the rotation of the constrained object does not change. This means you’ll be able to manually change the rotation of the object as you see fit.
#### Offset
The Offset toggle allows the constrained object to move along the path, but from its current, offset position.
# IK Constraint
Source: https://rive.app/docs/editor/constraints/ik-constraint
Use IK constraints to control bone chains with a target.
## Forward vs Inverse Kinematics
Most skeletal animation in Rive uses **Forward Kinematics**. With Forward Kinematics, you pose a bone chain by rotating each bone. Child bones move based on the rotation of their parents.
**Inverse Kinematics** works in the opposite direction. Instead of rotating each bone manually, you place a target at the end of the chain and Rive solves the bone rotations needed to reach it.

IK is useful for rigs where the end of a chain needs to follow something else. For example, you might use IK to make a character point at an object or keep a character's feet planted on the ground.
## Creating an IK constraint
To use IK, you need a bone chain and a target. The target can be any object, though in most cases you'll want to use a group with its [Style set to Target](/docs/editor/fundamentals/groups#group-style).
Use the **B** shortcut to create a [bone chain](/docs/editor/manipulating-shapes/bones#how-to-create-bones).
Then use the **G** shortcut to create a [group](/docs/editor/fundamentals/groups).
With the group selected, set **Style** in the Inspector to **Target**. A target group stays selectable, even when other objects are above it. See [Targets](/docs/editor/fundamentals/groups#target).

Select the last bone you want the IK constraint to affect. In the Inspector, use the **Constraints** section to add an **IK** constraint.

Open the constraint flyout menu and use the target button to select the target group you created in step 1.

Move the target group. The affected bones should rotate toward the target.

## IK Properties
### Bone Count
Use **Bone Count** to set how far up the bone chain the IK constraint should reach.
When the target is selected, bones affected by the IK constraint are highlighted.

### Invert Direction
Use **Invert Direction** to swap the angle used to solve the IK chain.

### Strength
Use Strength to control how much the affected bones follow the target. A Strength of 0% means the target does not affect the bones.
Strength can be animated like most properties in Rive. This is useful for blending between Forward Kinematics and Inverse Kinematics, or for blending between multiple IK constraints with different targets.

### Constraints order
The order of constraints matters. If a bone has two IK constraints and both have a Strength of 100%, the lower constraint overrides the one above it.
If the constraints use lower Strength values, Rive blends between them. Drag constraints in the Inspector to change their order.

## Multiple IK constraints and nested targets
You can use multiple IK constraints to create more complex rigs.
For example, a character leg might use one IK constraint on the foot and another IK constraint on the leg bones. The leg target can be a child of the foot target, so moving the foot target also moves the leg target.

# Rotation Constraint
Source: https://rive.app/docs/editor/constraints/rotation-constraint
The Rotation Constraint allows you to set limits on an object's rotation and/or copy the rotation properties from a target object. These properties can be independently activated.
## How to create a Rotation Constraint
Use the Constraints section of the Inspector to add a Rotation Constraint to an object.

Use the new constraint's fly-out menu to select a target for this constraint.

Manipulating the target object now causes the constrained object to copy Rotation properties.

## Strength
The Strength property determines how much the constrained object is affected.
A Strength of 0% means the constraint won't have any effect.
A Strength of 50% means half the value from the target will be applied.
## Transform Space
### Source Space
Choose whether this constraint should use World or Local coordinates for the Source Space.
### Destination Space
Choose whether this constraint should use World or Local coordinates for the Destination Space.
### Min/Max Space
Choose whether this constraint should use World or Local coordinates for the Min/Max Space.
## Offset
Allows the constraint owner to be manually offset from the constraint source.

## Copy
Define the rate at which the rotation property is copied.
## Min/Max
Use the numerical values to define the minimum and maximum limits of the constraint.
# Scale Constraint
Source: https://rive.app/docs/editor/constraints/scale-constraint
The Scale Constraint allows you to set limits on an object's scale and/or copy the scale properties from a target object. These properties can be independently activated.
## How to create a Scale Constraint
Use the Constraints section of the Inspector to add a Scale Constraint to an object.


Use the new constraint's fly-out menu to select a target for this constraint.

Manipulating the target object now causes the constrained object to Scale properties.
## Strength
The Strength property determines how much the constrained object is affected.
A Strength of 0% means the constraint won't have any effect.
A Strength of 50% means half the value from the target will be applied.
## Transform Space
### Source Space
Choose whether this constraint should use World or Local coordinates for the Source Space.
### Destination Space
Choose whether this constraint should use World or Local coordinates for the Destination Space.
### Min/Max Space
Choose whether this constraint should use World or Local coordinates for the Min/Max Space.
# Scroll Constraints
Source: https://rive.app/docs/editor/constraints/scroll-constraint
Scrolling comes to Rive in the form of two new constraints; one to add touch-based scrolling to overflowed content, and another to create a scroll bar. Both constraints work in conjunction with the existing layout components. We plan to provide generalised scrolling components for quicker setup in future.
Scroll wheel/trackpad gestures aren't currently supported but are on our v1 Roadmap.
***
## Content Scrolling
To create a content scroll region, set up a hierarchy that includes:
* Scroll view - the Layout that defines the area that is the scroll region
* Scroll content - the Layout that contains the items to be scrolled (this is the Layout you will apply the Scroll Constraint to). The scroll amount will be determined by the size of this Layout. Typically you want to set this Layout to hug (or fixed)
* Scroll items - the Layout items to be scrolled
Select your Scroll content Layout and use the add action within the constraint inspector to add a Scroll Content constraint.
Once added, use the options fly-out to adjust the Scroll content properties.
#### Direction
**Vertical** - Only scrolls in the vertical direction
**Horizontal** - Only scrolls in the horizontal direction
**All** - Scrolls in both directions
#### Scroll Percent X/Y (Animatable)
This property allows you to set the percentage scroll of the content where 0% is scrolled to the top/left and 100% is scrolled to the bottom/right. This property works when the content is set to scroll in one or all directions and can be keyed on the timeline
#### Scroll Index (Animatable)
This property allows you to set the 0 based index of the Scroll item to scroll to. This only works when scroll is set to either Vertical or Horizontal and can be keyed on the timeline.
Scroll Percent and Scroll Index both control the offset within the Scroll content area. As such, you should only set one or the other because there will be contention when both are set at the same time. For example, you should only key one of these values on a given frame in your timeline.In order to use Scroll Percent and Scroll Index together with physical scroll dragging, you should create an empty "reset" timeline/state which you should transition to as soon as the scroll area is interacted with. One way to do this is to add a Mouse down listener to the Scroll View.
#### Physics
**Elastic** - An iOS style scroll with deceleration and rubber banding at the edges
**Clamped** - A basic drag and drop with no physics
#### Snap
When enabled, the scroll content will always settle with a whole item at the top/left of the scroll area.
Once you have applied your desired properties, switch to animate mode and start playback of a State Machine to preview the applied scroll constraint. You should be able to click/drag/release within the Scroll view area to manually control the scroll.
***
## List Scrolling
In order to scroll Lists, use the same setup as described above in Content Scrolling, but rather than adding multiple Scroll items to the Scroll content layout, you can add a single List. See [Data Binding Lists](/docs/editor/data-binding/lists) for more information.
There are some additional properties that apply when scrolling Lists. These will only be enabled once a List has been added to the Scroll content.
#### Virtualize
When enabled, the List will only generate Artboard components for the items currently in the Content view area. This improves performance when scrolling lists containing a large number of items. Note that virtualization can only be applied in one direction (either vertical or horizontal, not both).
#### Carousel
When enabled, the List will scroll endlessly in either direction. In order to use Carousel mode, Virtualize must be enabled.
***
## Creating a Scroll Bar
To create a content bar, set up a hierarchy that includes:
* Scroll Bar - the Layout that defines the area that is the scroll bar and track
* Scroll Thumb - the Layout that defines the draggable scroll thumb
To create a scroll bar, select a Layout Component acting as the scroll bar thumb. From the constraints inspector, add a Scroll Bar Thumb constraint.
Use the target button within the options panel to connect a scrolling Layout. As described in the Scrolling Content section above, this should be the Layout that your Scroll Constraint was applied to.
# Transform Constraint
Source: https://rive.app/docs/editor/constraints/transform-constraint
The Transform Constraint allows its owner to copy all the transformation properties from a target object, regardless of their hierarchical relationships. These properties include Position, Rotation, and Scale.
## How to create a Transform Constraint
Use the Constraints section of the Inspector to add a Transform Constraint to an object.

Use the new constraint's fly-out menu to select a target for this constraint.

Manipulating the target object now causes the constrained object to copy Position, Rotation, and Scale properties.

## Strength
The Strength property determines how much the constrained object is affected.
A Strength of 0% means the constraint won't have any effect.
A Strength of 50% means half the value from the target will be applied.

## Transform Space
### Source Space
Choose whether this constraint should use World or Local coordinates for the Source Space.
### Destination Space
Choose whether this constraint should use World or Local coordinates for the Destination Space.
## Example: mechanical arm
Consider the package resting on the table and the mechanical arm below.
Add a Transform Constraint to the package and a target group at the end of the arm.

The target group is a child of the arm hierarchy, so it moves with the arm.
With a Strength of 100%, all the transform properties of the package match the target. Notice how the package moves and rotates correctly with the movement of the arm.

Set the Strength to 0% to make the arm drop the package.
# Translation Constraint
Source: https://rive.app/docs/editor/constraints/translation-constraint
The Translation Constraint allows you to set limits on an object's position and/or copy the position properties from a target object. These properties can be independently activated.
## How to create a Translation Constraint
Use the Constraints section of the Inspector to add a Translation Constraint to an object.

Use the new constraint's fly-out menu to select a target for this constraint.

Manipulating the target object now causes the constrained object to copy Position properties.

## Strength
The Strength property determines how much the constrained object is affected.
A Strength of 0% means the constraint won't have any effect.
A Strength of 50% means half the value from the target will be applied.
## Transform Space
### Source Space
Choose whether this constraint should use World or Local coordinates for the Source Space.
### Destination Space
Choose whether this constraint should use World or Local coordinates for the Destination Space.
### Min/Max Space
Choose whether this constraint should use World or Local coordinates for the Min/Max Space.
## Offset
Allows the constraint owner to be manually offset from the constraint source.

## Copy X \&Y
Allows you to decide if the constraint owner will copy the translation in the X and Y direction. Additionally, use the numerical value to define the rate at which it copies the value.
## Max/Min
Use the numerical values to define the minimum and maximum limits of the constraint.
# Binding Data
Source: https://rive.app/docs/editor/data-binding/binding-data
Binding is how you connect data to properties and elements in your Rive file.
For example, if you have a `teamName` property bound to a text run in your scene, changing the `teamName` value updates the displayed text automatically.
You can change property values in the editor or at runtime, allowing you to create dynamic experiences that adapt to each user.
Bindings are not limited to visual properties. You can bind values throughout your file, including transforms, animation speeds, [converter values](/docs/editor/data-binding/converters#converter-types), [script inputs](/docs/scripting/script-inputs#data-binding-inputs), [transition condition values](/docs/editor/state-machine/transitions), and more.
This allows the same data to drive behavior, animation, and logic across your scene, helping keep everything in sync.
## Binding a Property
Let's control the `X` position of a circle using `circleX`, which is a Number property in our View Model.
With your circle selected, right-click the `X` field, and select **Data Bind**.
In the **Property** dropdown, select `circleX`.
The `X` field should now be highlighted in green, indicating that it's bound to a value.
The circle's horizontal position is now driven by the `circleX` property. Any time the property's value changes, the circle updates automatically.
If the field has a yellow border, there is a problem with the binding.
In most cases, the property type is incompatible with the target value. For example, the `X` position of an element can only be bound to a Number property.
**Previewing Bindings**
By default, your data only controls your elements when the state machine is playing.
To preview your data-bound values, turn on the **Data Binding Preview Toggle**.
### Updating a Binding
Right-click the bound element and select **Update Bind**.
### Removing a Binding
Right-click the bound element and select **Unbind**.
## Binding Options
Each binding has the following properties.
### Property
The View Model property the target is connected to.
When binding global properties, select the **Globals** icon.
### Path
When binding properties in nested View Models, there may be multiple properties with the same name available.
The path determines which specific property instance the binding should use.
See also [Absolute vs Relative Binding](#absolute-vs-relative-binding).
### Bind Direction
By default, the View Model property (**source**) controls the bound element or value (**target**), but it can also work the other way around.
#### Target to Source
When a binding has `Target to Source` toggled on, a change to the element will update the view model property.
Let's say you have a walk cycle animation and you want to track the exact Y position of the head as it bobs up and down.
With the head's `Y` position bound to `headY` using **Target to Source**, the `headY` property updates automatically as the head moves.
#### Bidirectional
A change to the view model property will update the element **AND** a change to the element will update the view model property.
Clicking the **Bidirectional** toggle multiple times will cycle between **Bidirectional (prefer target value)** and **Bidirectional (prefer source value)**.
In rare cases, both the source and target may change at the same time. This setting determines which value wins when a conflict occurs.
Let's say you're building a game of checkers and need to create logic for dragging and dropping each piece.
The initial position of each piece is determined by the `piecePositionX` and `piecePositionY` properties (source to target).
As the piece is dragged, the piece itself (target) updates those property values (target to source).
### Bind Once
When **Bind Once** is enabled, the binding only applies the value a single time when the scene starts or the binding is created.
After the initial value is applied, future changes to the source or target are ignored.
Let's say you're creating a particle effect and want each particle to start with a random rotation value.
You could bind the particle's rotation to a random number property using **Bind Once**. Each particle would receive its initial rotation value, but future changes to the property would not affect existing particles.
### Absolute vs Relative Binding
By default, new data binding connections are created as absolute binds.
* **Absolute Binding**: The binding points to a specific property instance within the View Model hierarchy.
* **Relative Binding**: The binding searches for a property with a matching name relative to the current context.
# Controlling Data
Source: https://rive.app/docs/editor/data-binding/controlling-data
Update and react to data from within Rive or from your application code.
Once you've bound data to properties in your file, changing that data will automatically update the connected elements.
You can control data from within your Rive file, or from your application at runtime.
## Controlling data within Rive
Rive includes several ways to update data directly inside your file:
Update property values when [transitions](/docs/editor/state-machine/transitions#actions) or [state changes](/docs/editor/state-machine/states#actions) occur.
Update data in response to interactions and events.
Write custom logic that reads and updates data.
Update data using an element's property value.
Animate values and bind them back to data.
## Controlling data from code
Your application can also update data at runtime. For example, you might:
* Update a player's score
* Change a username or profile image
* Display live stock prices
* Trigger UI states based on user interaction
See the [Runtime Data Binding Overview](/docs/runtimes/data-binding) for implementation details.
# Converters
Source: https://rive.app/docs/editor/data-binding/converters
Transform and adapt data before it’s applied to a binding using built-in and custom converters.
Converters transform values before they are applied to a binding.
Use them to adapt your data to match the property you're binding to—for example, converting numbers to strings, mapping values between ranges, or smoothing changes over time.
## Adding a Converter
In the Assets panel, click the `+` button and select a [converter type](#converter-types).
With the converter selected, update the settings in the right sidebar.
Most options can be data bound.
When setting or updating a binding, set the **Converter** field..
## Converter Types
Converters can be used individually or combined into [groups](#converter-groups) to perform more complex transformations.
Each converter has its own set of properties, many of which can be bound to other View Model properties.
This allows converter behavior to update dynamically. For example, you could bind the maximum value of a range map, a multiplier in a formula, or the padding amount in a string converter.
| Category | Converter | Description |
| -------- | --------------------------------------- | ----------------------------------------------- |
| String | [Pad](#pad) | Add padding to a string |
| | Trim | Remove leading or trailing whitespace |
| | Convert to String | Convert a value to a string |
| | Remove Trailing Zeros | Remove unnecessary decimal zeros |
| Number | Round | Round to the nearest value |
| | Calculate | Perform simple calculations |
| | [Range Map](#range-map) | Map a value from one range to another |
| | [Interpolator](#numeric-interpolator) | Smoothly interpolate between values |
| | [Formula](#formula) | Evaluate a custom expression |
| | Convert to Number | Convert a value to a number |
| Boolean | Toggle | Invert a boolean value |
| List | [Number to List](#number-to-list) | Generate a list of components based on a number |
| | List to Length | Get the number of items in a list |
| Color | [Interpolator](#color-interpolator) | Smoothly interpolate between colors |
| Script | [Converter Scripts](#converter-scripts) | Create your own custom converters |
### Pad
Pads a string to a target length by repeating a value at the start or end.
If the string is shorter than the specified length, the pad value is repeated and added until the target length is reached.
**Example:**
* **Value:** 1
* **Pad:** "0"
* **Direction:** Start
* **Length:** 3
**Result**: "001"
***
### Range Map
Maps a number from one range to another.
Use Range Map when you want to take an input value and convert it into a different scale.
**Example**
Convert a slider value (0–100) into an opacity value (0–1):
* **Input**: 50
* **Input range**: 0 → 100
* **Output range**: 0 → 1
**Result**:
0.5
***
### Numeric Interpolator
Smoothly transitions between number values over time, easing changes instead of jumping instantly.
***
### Formula
Formula lets you perform custom calculations using values from your View Model or the input.
You can create a formula in two ways:
**Writing it directly**
```lua theme={null}
random({{NumberConvertersVM/circleX}} / {Input}) + 2
```
`{Input}` refers to the incoming value, while `{{...}}` references View Model properties.
**Using the editor**
Click the + button to add values, operations, and functions. This builds the formula for you and generates the equivalent expression.
**Formulas don't need inputs**
The following formula would output a random number, regardless of input.
```lua theme={null}
random(2)
```
#### Random Mode
When using `random()` in your formula, the Random Mode determines when a new random value is generated.
* **Once** — Generates a random value only when the formula first runs
* **Source Change** — Generates a new value each time the input changes
* **Always** — Continuously generates new values while the input changes and during interpolation
***
### Number to List
The Number to List converter allows you to generate a specified number of Components based on a number.
See [Lists](/docs/editor/data-binding/lists#view-model-number-with-converter) for more information.
***
### Color Interpolator
Smoothly transitions between color values over time, easing changes instead of jumping instantly.
***
### Converter Scripts
Scripting lets you create your own custom converters when you need behavior that isn’t covered by the built-in converters.
See [Converter Scripts](/docs/scripting/protocols/converter-scripts) for more information.
## Converter Groups
Converter groups let you chain multiple converters together, where the output of one becomes the input of the next.
In the Assets panel, click the `+` button and select Converter > Group.
With the group selected, add the `+` button in the right sidebar and select an existing or new converter.
When binding a value, your converter group can now be used just like an individual converter.
### How execution works
Converters run from top to bottom:
* The first converter receives the original input
* Each converter passes its result to the next
* The final output is the result of the last converter
### Reordering converters
Drag a converter up or down to change when it runs.
Earlier converters affect all converters that follow.
# Enums
Source: https://rive.app/docs/editor/data-binding/enums
Use enums to control states, modes, and variants by selecting from a predefined set of options.
An enum lets you choose one value from a predefined set of options.
Use enums when a property should only ever be one of a few known values—like modes, states, or variants—instead of allowing any arbitrary value like a string.
For example, instead of using a string like "left", "center", or "right", an enum guarantees the value is always one of those valid options.
Enums can be:
* System enums — built-in sets of options used by the editor (for example, Horizontal Align)
* [Custom enums](#custom-enums) — your own defined sets of options for your specific use case
**Why not use strings or numbers?**
Imagine a calendar app that shows different backgrounds based on the day of the week.
You could use a string like "Monday", but it breaks if someone sets it to "Mon".
You could use a number, but it’s unclear whether the week starts on Sunday or Monday—or whether the index starts at 0 or 1.
With an enum, the value must be one of the defined options, so it’s always valid and unambiguous.
## Adding an enum property
Create a new enum view model property inside your view model.
Click the dropdown next to the property name and select an enum type.
Select your enum view model property and in the right sidebar, select the default value.
## Binding enums
Enums can be bound to editor properties that use the same set of options. For example, an enum named “Layout Direction” can be bound to a layout’s Direction property.
When binding enums, the System Enum to Uint converter is required and is applied automatically.
## Custom enums
Open the Data panel, click the `+` icon and select enum.
With your enum selected, click the `+` icon in the right sidebar.
Assign your custom enum to your enum view model property.
## Controlling Solos with enums
Use an enum to control which Solo is active by mapping each enum value to a Solo.
Create a [custom enum](#custom-enums) with values that match the **names and order** of your Solos.
[Export the names](/docs/editor/exporting/exporting-names) of the objects used by the Solo so they are accessible at runtime.
Bind the enum to the Solo’s **Active** property and apply a **Convert to Number** converter.
# Lists
Source: https://rive.app/docs/editor/data-binding/lists
Use Data Binding to generate lists at runtime
A List is a way to display a set of items that are dynamically generated based on bound data values that you set up in your View Models. This allows you to build Rive files that can change in real time based on updates to those values. You can use Lists to create:
* Menus with a dynamic amount of options
* Product listings
* Notifications or activity feeds
* Chat messages
* Dropdown menus
* Contacts, friends, or followers lists
* High scores, tables, and more
***
## Artboard Lists
Artboard Lists enable you to generate a number of list items using an Artboard to represent each item in your List. Artboard Lists must be added as children of an Artboard or Layout.
To add an Artboard List to the stage, first select either an Artboard or a Layout. In the inspector on the right side of the editor, you will see a button to add an Artboard List. Click it to add the List to your hierarchy. It will appear as a child of the Artboard or Layout you had previously selected.
Once the Artboard List is added to your hierarchy, you can select it and see its inspector.
The next step is to bind data to it using Data Binding. This will determine the content and number of items in your List. There are 2 ways to generate List content:
* Using the View Model List property
* Using a View Model Number property together with a Number to List converter
## View Model List Property
Before continuing, it's important to understand the fundamentals of Data Binding, in particular, what a View Model is, how to create one and how to bind it to an object's properties. Learn more in the [Data Binding Overview](/docs/editor/data-binding/overview).
A View Model List is a property that can contain a dynamic number of items which each represent a View Model instance. In order to be used in a List, the View Model must be bound to an Artboard.
To Create a View Model List and bind it to an Artboard List:
Navigate to the Data tab in the Editor. Click the + button beside View Models to create a View Model (this will represent your List item)
Bind that View Model to an Artboard that you want to use to render your List item. This is where you may also want to create additional View Model properties and bind those to object properties on that Artboard.
Click the + button beside View Models to create another View Model (this will be the View Model that contains your List and should be bound to the Artboard where you want your List to render)
Select the newly created View Model and click the + button next to it. From the popout select **List**. This adds a List property to the View Model
Select the List property and in the inspector on the right, you can add items by clicking Add List item Once a List item is added, you can click the settings button to the left of its name in order to set the View Model and View Model instance for that item
After adding your List items, go back to the Hierarchy tab, select the Artboard List, and from the Artboard List property dropdown, select the ViewModel List property you created above.
Run the state machine and you should see your list items. Remember that the layout will be determined by the Artboard List's parent, so modify the parent's settings in order to tweak your List's layout
Rive's runtimes provide [APIs to modify the List and List items at runtime](/docs/runtimes/data-binding#lists) (for example, adding or removing items).
## View Model Number with Converter
The second way to populate your list is by specifying the number of items you want in your List along with the ViewModel (Artboard) you want to instance. This can be done using a View Model Number property in combination with the new Number to List converter.
To Create a View Model Number to List converter and bind it to an Artboard List:
Navigate to the Data tab in the Editor. Click the + button beside View Models to create a View Model (this will represent your List item)
Bind that View Model to an Artboard that you want to use to render your List item. This is where you may also want to create additional View Model properties and bind those to object properties on that Artboard.
Click the + button beside View Models to create another View Model (this will be the View Model that contains your Number property and should be bound to the Artboard where you want your List to render)
Select the newly created View Model and click the + button next to it. From the popout select Number. Select the newly created Number and in the inspector, set the number of items you want in your List
Click the + button and choose **Converters > List > Number to List**.
Select the created converter and in the inspector, choose the View Model that you created earlier that represents your List item. The converter will convert the Number of items to actual Artboard items
After adding your Number property and converter, go back to the Hierarchy tab, select the Artboard List, and from the Artboard List property dropdown, select the ViewModel Number property you created above. The Combobox will show a yellow outline. Right click the Combobox and choose Update Bind. In the converter field, select the Number to List converter you created. The yellow outline should change to green.
Run the state machine and you should see your list items. Remember that the layout will be determined by the Artboard List's parent, so modify the parent's settings in order to tweak your List's layout
## View Model List Item Index
There may be times where it is useful for the Artboard to know at which index it exists within its parent List. This is available using the View Model list item index property.
This can be added to your item's View Model by clicking the + button and selecting **List Attributes > Index**.
This property can then be bound to an object's properties and used directly (for example, to change the position of an object based on the index), used together with a converter (to display the index value as a string) or used as a condition in a state machine (to provide different behavior depending on the item's index).
When using item index with the [List property](/docs/editor/data-binding/lists#view-model-list-property) and list items, be aware that if more than 1 list item is bound to the same View Model instance, they will share the same index value
## Lists & Scrolling
If you add your Artboard List as a child of a Layout, your List items will behave as children of its parent's Layout. This means things like direction, wrapping, padding, gap, alignment, etc. are all dictated by the parent Layout's properties.
In addition, if you have applied a [Scroll Constraint](/docs/editor/layouts/scrolling) to the parent Layout, the List items will have the ability to scroll out of the box without any additional setup.
There are additional Scroll properties that can be applied when scrolling Lists. See [List Scrolling](/docs/editor/layouts/scrolling#list-scrolling) for more details.
# Migration Guide
Source: https://rive.app/docs/editor/data-binding/migration-guide
Migrate from State Machine Inputs and Listening to Events at runtime to Data Binding
Data binding replaces both **state machine inputs** and **runtime event listeners**, and also removes the need to directly modify elements like text runs from code.
It provides more data types in a more flexible, consistent system for both sending data into Rive and reacting to changes from Rive.
| | Inputs | Events | View Model Properties |
| ---------------------- | :----- | :----- | :-------------------- |
| Floating point numbers | ✅ | ✅ | ✅ |
| Booleans | ✅ | ✅ | ✅ |
| Triggers | ✅ | ❌ | ✅ |
| Strings | ❌ | ✅ | ✅ |
| Enumerations (Enums) | ❌ | ❌ | ✅ |
| Colors | ❌ | ❌ | ✅ |
| View Model Nesting | ❌ | ❌ | ✅ |
| Lists | ❌ | ❌ | ✅ |
| Images | ❌ | ❌ | ✅ |
| Artboards | ❌ | ❌ | ✅ |
You do **not** need to update existing files. Inputs and events will continue to work as expected. Data binding is recommended for new work and future updates.
## Migrating from Deprecated Features
### State Machine Inputs
State machine inputs were previously the main way to control animations from code.
With data binding, you instead expose **view model properties** that can drive State machine transitions, Blend states, and Any bindable property in the editor
Open the **hamburger menu** in the editorSelect **Convert Inputs to View Models**Update your runtime code to set values on the view model instead of inputs
**Why are State Machine Inputs deprecated?**
Inputs are limited to driving state machine transitions and must be used as-is.
View model properties:
* Can drive more than just transitions
* Can be transformed using [Converters](/docs/editor/data-binding/converters)
* Can be shared across multiple parts of a file
* Provide a more flexible and scalable data model
### Communicating with Code via Events
Events were previously used to send information from a Rive file back to runtime code.
With data binding, you instead **listen to changes on view model properties**, including trigger and lists.
**Why is listening for Events at runtime deprecated?**
Events had several limitations:
* Difficult to pass dynamic or changing data
* Required manual handling to work across nested artboards
* Represented a one-time signal rather than a persistent value
View model properties always reflect the latest value and can be observed directly.
Instead of listening for an event:
* Create a **view model property**
* Update it from your Rive file (via animation, listener, or script)
* Subscribe to that property in your runtime
For more info, see [Data Binding](/docs/runtimes/data-binding)
***
### Updating Text Runs at Runtime
Previously, updating text required:
* Knowing the exact **name**
* Knowing the **hierarchy/path**
* Accessing the text run directly from code
This approach was brittle and easy to break when files changed.
With data binding:
* Bind a **string property** to a text run
* Update the value through the view model
This keeps your runtime code stable even if the structure of your file changes.
## Constraints vs Data Binding
Constraints are still fully supported and remain the best option for many use cases.
### When to use constraints
Use constraints when:
* You are linking one object directly to another
* The relationship is purely visual or spatial
* You want a simple, editor-only solution
### When to use data binding instead
Use data binding when:
* The value needs to come from **runtime code**
* Multiple elements depend on the same value
* You want to introduce **logic or transformation** (via [Converters](/docs/editor/data-binding/converters))
* The relationship isn’t strictly object-to-object
**Example:**
You could:
* Use a **constraint** to link the rotation of multiple wheels together
Or:
* Use a **data-bound `rotation` property** that:
* Is controlled at runtime
* Drives all wheels
* Can be reused elsewhere (speed, effects, UI, etc.)
Data binding is more flexible, while constraints are more direct.
# Data Binding Overview
Source: https://rive.app/docs/editor/data-binding/overview
Connect editor elements to data and code using View Models
Data binding connects data stored in View Models to properties in your Rive scene.
When data changes, your scene updates automatically. Likewise, changes made in your scene can be written back to your data.
For example, you might:
* Bind an enemy's X and Y position so it can be controlled at runtime.
* Bind the color of multiple icons to a single color property.
* Bind a `health` value to a health bar in the UI and to the character.
* Swap images, artboards, or components based on your application's state.
## Why Use Data Binding?
Data binding lets you organize your data independently from your scene hierarchy.
For example, you might have a `health` property stored at the top level of your data, while the health bar that displays it is nested several layers deep inside components.
Once the property is bound, the hierarchy no longer matters. You can move elements, reorganize components, or rename properties without rewriting runtime code.
## Core Concepts
1. [View Models](/docs/editor/data-binding/view-models) define the structure of your data.
2. View Model Instances store actual values for that data.
3. [Bindings](/docs/editor/data-binding/binding-data) connect View Model data to properties in your scene.
4. [Data can be updated](/docs/editor/data-binding/controlling-data) from the Editor, runtime code, state machines, or scripting.
5. Listeners, runtime code, and scripts can respond when data changes.
6. When data changes, bound elements automatically update to reflect the new values.
# Property Groups
Source: https://rive.app/docs/editor/data-binding/property-groups
Create local values that can be keyed, animated, and bound to View Model properties.
Property Groups are local, artboard-level values that can be keyed, animated, and bound to View Model properties.
View Model properties are global and shared, while timelines and state machines are local. Property Groups bridge that gap, allowing local animation and logic to read from or drive global data.
## Creating a Property Group
Select your Property Group tool and click the stage to add one to your artboard.
Select your Property Group and in the right sidebar, click the `+` icon and select your property type.
## Common Use Cases
### Controlling a View Model Property with a Timeline
You can’t key a View Model property directly in the timeline. Instead, you key a Property Group value, then bind that value to a View Model property.
This lets you use [Keyframes](/docs/editor/animate-mode/keys) in a timeline to update View Model data at runtime.
**Why can't I key View Model Properties directly?**
View Model properties represent global data shared across your app, while keyframes are local to a timeline—so they can’t be keyed directly.
With the Property Group selected, move the timeline playhead and change the property value to create keyframes.
Right-click the property and select the View Model property you want to control. Set the direction to Target → Source so the keyed value drives the View Model property.
When you run your state machine, the View Model property updates based on the keyed Property Group values.
### Controlling one View Model Property with another
You can't directly control one View Model Property with another View Model Property. Instead, you can use a Property Group value that reads one View Model Property and sets another.
**Why can't I control one View Model property with another?**
Allowing View Model properties to control each other would create hidden dependencies and update loops, so relationships between values are handled explicitly through bindings.
In this example we'll have two Number View Model Properties called `myNumber` and `myOtherNumber`, which will be `myNumber` doubled.
Create a new Converter of type Numeric > Formula and add a number value, a multiply operation, and another number.
Bind the first number to `myNumber`.
Select the Property Group, right-click the property, and bind it to the `myOtherNumber` value using the converter. Set the direction to Target → Source so the converted value drives the View Model property.
When you run your state machine, you should see that `myOtherNumber` is double `myNumber`.
# View Model Properties
Source: https://rive.app/docs/editor/data-binding/property-types
A view model property is one piece of data within a view model. Developers might think of this as similar to a field in object-oriented programming. Properties have a data type, which is selected when they are created, and a name which can be referenced in code. Each property can be bound to different editor elements of the same type or used as conditions within a state machine.
## Adding View Model properties
In the Data panel, click the **Add View Model Property** button next to the view model name and select your property type.
| Type | Description |
| ------------------------- | -------------------------------- |
| Number | Numeric value |
| String | Text value |
| Boolean | True/false value |
| Color | RGBA color value |
| [Trigger](#trigger) | Fire-and-forget event |
| [Enum](#enum) | One value from a predefined list |
| [Image](#image) | Image asset reference |
| [Font](#font) | Font asset reference |
| [Artboard](#artboard) | Artboard reference |
| [View Model](#view-model) | Nested View Model instance |
| [List](#list) | Collection of view models |
## Property Types
### Trigger
Trigger properties represent fire-and-forget events. Use them when you want to signal that something happened, such as a button press or one-time action.
### Enum
Select from a fixed set of options to control states and variants. Use enums when the possible values are known ahead of time.
[Learn more](/docs/editor/data-binding/enums) about enums.
### Image
Image properties store a reference to an image, allowing you to change which image is displayed.
They are typically bound to image nodes in your design.
Use image properties when each instance needs its own image, such as user avatars, thumbnails, or dynamically loaded content.
For example, in a game or social UI, each player can have their own avatar by binding a different image to the same property.
Image properties affect a **single instance**.
If you need to update an image globally across your entire file, use [asset loading instead](/docs/runtimes/loading-assets). Asset loading replaces the underlying asset, updating all instances that use it.
### Font
Font properties store a reference to a font, allowing you to change which font is displayed on a specific text style.
Font properties affect a **single instance**.
If you need to update a font globally across your entire file, use [asset loading instead](/docs/runtimes/loading-assets). Asset loading replaces the underlying asset, updating all instances that use it.
### Artboard
Artboard properties let you reference an artboard and dynamically swap it at runtime.
The artboard can come from your current Rive file or be loaded from another .riv file.
To use an artboard as a property, it must first be [converted to a Component](/docs/editor/fundamentals/components#creating-a-component).
### View Model
View Model properties store a reference to a View Model instance.
They are used to create nested data structures by connecting one View Model instance to another.
For example, a `Player` View Model could contain a `Team` View Model property, allowing you to access properties such as the team's name, logo, and colors.
You can bind to and update properties on nested View Models just like top-level properties.
If you want to assign a View Model instance to a nested component, that instance must be referenced from your main View Model.
### List
List properties store collections of view model instances, which can be associated with specific artboards.
Lists are commonly used for repeating content, such as inventory items, players, messages, or generated UI elements.
For more information, see [Lists](/docs/editor/data-binding/lists).
# Stateful Components
Source: https://rive.app/docs/editor/data-binding/stateful-components
Stateful components let you expose specific view model properties directly on a nested component, so each instance can have its own values. This makes it faster to create and iterate on designs that reuse the same component with different content or styling — without wiring up a separate nested view model instance for each one.
### Setting Up a Component for Stateful Use
Before a component can be used statefully, its artboard needs a view model with the properties you want to expose.
1. Create or open the artboard you want to use as a component.
2. Add a view model and bind the relevant properties to elements in the artboard (for example, a label string bound to a text run, or a background color bound to a fill).
3. Mark the artboard as a component using the component toggle in the inspector, or press Shift + N.
4. In the inspector, find the Properties section. Click + to add the view model properties you want to expose.
5. For each property, choose whether it's an Input or an Output:
* Input — the value can be set on each instance when nested as a stateful component.
* Output — the value is read-only when nested; it reports back to the parent.
### Using a Stateful Component
When you nest a component (press N and select it from the menu), it comes in as a stateless component by default. Switch it to a stateful component in the inspector.
Once switched to stateful, the nested view model instance selector goes away, and you'll see the exposed properties instead. Set override values directly in the inspector. Each instance of the component can have its own values for those properties.
Because the values are owned by the component instance, you don't need a separate view model instance for each one.
### Nesting Stateful Components
Stateful components can be nested inside other components. For example, a card component might contain a stateful button component.
If you want the card's parent to be able to override the button's properties, add those properties to the card's view model and data bind them to the button's stateful properties. Once added to the card's Properties panel as inputs, they'll be available to override when the card is nested.
### Output Properties
Output properties report a value out of a component when it's nested. They appear with a lock icon in the inspector and are marked as read-only — you can interact with the component, but you can't set the value from outside.
To read an output value in the parent artboard, add a property to the parent's view model and data bind it (target to source) to the component's output property. The value will update in real time as the state machine runs.
### Keying Input Properties on Timelines
Input properties can be keyed on a timeline. Open the timeline view with a stateful component selected and you'll see a key indicator next to each exposed input property. Key the values at different points in the timeline to animate them.
# View Models & Instances
Source: https://rive.app/docs/editor/data-binding/view-models
A View Model defines the structure of your data. It acts as a reusable blueprint that contains a collection of properties, such as a car's color, speed, and damage.
Each property has a type, such as Number, String, Boolean, Color, Enum, or List (For more information, see [View Model Properties](/docs/editor/data-binding/view-model-properties)).
View Models do not store values themselves. Instead, values are stored in one or more View Model Instances that are created from the View Model.
For example, if you were building a racing game, you might create a Car View Model with the following [properties](/docs/editor/data-binding/view-model-properties):
```jsx theme={null}
Car (View Model)
color: Color
damage: number
make: string
speed: number
```
A single View Model can be used to create many View Model Instances. Each instance shares the same structure but stores its own values.
```jsx theme={null}
Car1 (View Model Instance)
color: 0xFFFF0000
damage: 0
make: "Subaru"
speed: 0
Car2 (View Model Instance)
color: 0xFFFF00FF
damage: .9
make: "Honda"
speed: 0
```
The properties in your instances can then be bound to elements in your designs, allowing text, images, colors, animations, state machines, and other properties to react to data changes automatically.
### Creating a View Model
A View Model defines the structure of your data. Before creating View Model Instances or setting up data bindings, you'll first need to create a View Model and add properties to it.
New Rive files come with a default view model called "ViewModel1" that is already attached to the main artboard.
In the Data panel, click the `+` icon and select **View Model** or [**Global View Model**](#global-view-model-instances).
In the Data panel, click the **Add View Model Property** button next to the view model name and select your [property type](/docs/editor/data-binding/property-types).
## Creating and Editing Instances
Once your View Model has been created, you can create one or more View Model Instances that store actual values for those properties.
### Creating a View Model Instance
A View Model can have multiple instances. Each instance contains its own set of property values while sharing the same structure defined by the View Model.
When creating a new View Model, Rive automatically creates a View Model Instance named "Instance" so you can immediately begin testing bindings and previewing data.
Select a View Model in the Data panel, then click the Controls icon next to the instance name in the Inspector.
To see all of your view model instances and values at once, click the Open Table Data button.
Click the `+` icon to add a new instance.
Double click the instance's name to rename it.
With the instance selected, change any of its properties.
Note that this changes **only** the values on the selected view model instance.
### Removing Instances
Click the `-` icon next to the instance name to delete it.
Deleting a View Model Instance does not remove the View Model or its properties. It only removes that instance and its stored values.
### Exporting Instances
By default, exported View Model Instances are included in the .riv file. Disable Export Instance when the data is only needed during development, not in your application at runtime.
Let's say you're building a high score screen in Rive. While designing, you'll use fake score data, but when your game is live, the high scores will be populated using real data from a database or another source.
In this case embedding the example data just increases your .riv file size.
## Connecting View Model Instances to Artboards
Before binding data, you'll need to decide where the data should live.
Rive supports a few common patterns:
| Pattern | |
| :------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------- |
| [Global Instances](#global-view-model-instances) | Global view models and their properties are directly accessible to all artboards |
| [Instances attached to artboards](#instances-attached-to-artboards) | The view model instance is attached to the artboard and its values can be used by that artboard and its children |
| [Instances as nested properties](#instances-as-nested-properties) | The parent view model instance stores a reference to a nested view model instance |
| [Stateful nested components](#stateful-components) | Self-contained, reusable components that store their own unique data |
### Global View Model Instances
Global view models are not connected to a specific artboard. Instead, the view model instance and its properties are directly accessible to all artboards.
A game's main view model might contain a `brandColor` or `score` property. This property might be used by various nested components, like the header, on buttons, or on a high scores screen.
### Instances Attached to Artboards
View Model instances can be attached to a specific artboard or nested component. This is useful when only that artboard and its children need access to that data.
```
EnemyVM
├─ health
```
An Enemy view model might contain a `health` property. This property might be used by the enemy itself, but not by its parent.
### Instances as Nested Properties
View Models can have properties that are View Model instances.
In this pattern, the parent View Model stores references to child instances, either directly or through a [list](/docs/editor/data-binding/lists).
```
GameVM
├─ health
└─ score
└─ avatar
└─ inventory (stores an instance of InventoryVM)
InventoryVM
├─ Sword
├─ Shield
└─ Potion
```
This approach is useful when a parent artboard needs to manage a collection of items while still allowing each component to maintain its own data.
An inventory screen might contain a list of Item View Model Instances, with each inventory slot component bound to a different item.
### Stateful Components
A stateful component is an instance of a component that maintains its own data.
Unlike traditional component View Model Instances, these instances do not need to be created or referenced by the parent View Model. Instead their properties are exposed in the sidebar.
For more information, see [Stateful Components](/docs/editor/data-binding/stateful-components).
# Shape Builder
Source: https://rive.app/docs/editor/design/shape-builder
Merge and subtract vector shapes.
The Shape Builder tool lets you create new shapes by combining or subtracting vector shapes. Instead of editing paths manually, you can drag across regions to merge them or remove unwanted sections.
Select two or more vector shapes.
Click the **Shape Builder** tool from the **Create Tools** menu in the Toolbar or press **Shift** + **M**.
Drag across adjacent regions to merge them into a single shape.
Hold Alt (Windows) or Option (macOS) while dragging or clicking to remove regions from the shape.
# Embed URLs Overview
Source: https://rive.app/docs/editor/embed-urls/overview
Embed URLs are a fast, no-code way to share or embed your Rive files on the web.
Generating embed URLs is available on Voyager and Enterprise plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
Embed URLs let you quickly generate a public version of your file for embedding or sharing.
Unlike [inviting someone to collaborate](/docs/account-admin/workspaces/managing-workspace-members#inviting-members-to-a-workspace), an embed URL is a **snapshot** of your file at the moment it’s generated. If you make changes later, you’ll need to generate a new link to reflect those updates.
## Creating an embed URL
In the Rive editor, click the **hamburger menu** in the top-left corner and select **Generate Embed URL**.
In the dialog that appears, click **Generate Embed URL**.
Once the link is generated, select the option that best fits how you plan to use your file.
For more information on using embed URLs with tools like Framer, Webflow, and Notion, see [Integrations](/docs/integrations/overview).
### Embed URL types
* **Hosted link** — Opens your file on a dedicated Rive page with a framed viewer. Ideal for sharing with clients or stakeholders.
* **Embed link** — Displays your file without the surrounding Rive UI. Useful for platforms that automatically preview links (e.g. Notion, Telegram).
* **Embed code** — An iframe snippet for embedding your file into sites where you can edit HTML (e.g. WordPress).
* **Framer code (Deprecated)** — See the new official [Rive Framer Plugin](https://www.framer.com/marketplace/plugins/rive/).
### Embed URL options
* **Enable** — Turn the link on or off to control access.
* **Rive Renderer** — Use the Rive Renderer (recommended) or Canvas renderer.
* **Multi-touch** — Enable support for multi-touch interactions.
Certain features, such as [Vector Feathering](https://rive.app/blog/introducing-vector-feathering?utm_source=docs\&utm_medium=content), are only available using the Rive Renderer. See our [Feature Support](/docs/feature-support) page for more information.
## Sharing on Social Media
1. Copy the **Hosted Link**
2. Paste it into your favorite platform
3. See your Rive creation unfurl when you post
## Managing embed URLs
Visit the [Embed URLs](https://rive.app/account/links?utm_source=docs\&utm_medium=content) section of your settings to manage the links you've generated. You can disable an embed URL by setting its Active toggle to off.
# Audio Events
Source: https://rive.app/docs/editor/events/audio-events
Event-based audio provides the means to trigger sound effects within your animations and/or in response to user interactions. Just like existing events, they can be triggered by a keyframe on a timeline, during a state transition, or via a listener.
Audio events represent the first phase of audio features in Rive — they provide an ideal way to trigger sound effects that can be layered on top of each other.
Previously, triggering audio with events would require some work with one of the Rive runtimes and your application or game. The introduction of audio events directly inside the Rive editor streamlines the process of adding sound to your animations — further empowering designers, whilst simplifying the implementation for developers.
Audio events are ideal for triggering shorter sounds in response to user interactions or to complement character animations. Whilst longer form audio — such as background music and voice overs — can also be triggered with audio events, they lack a level of control to manipulate volume, panning, and more over time.
For use cases requiring greater control over volume and playback, consider using [Scripting](/docs/scripting/getting-started). Check out [AudioSound](/docs/scripting/api-reference/interfaces/audio-sound) and [AudioSource](/docs/scripting/api-reference/interfaces/audio-source) for more information.
## Working with Audio Assets
Audio events use sounds from the Assets panel. See [Importing Assets](/docs/editor/fundamentals/assets-overview#importing-assets) to import MP3, WAV, and FLAC files, or see [Audio Assets](/docs/editor/assets/audio) to browse Soundly sounds, create clips, and adjust asset volume.
## Creating an audio event
The simplest way to create an audio event is to drag your audio asset or clip directly from the [assets panel](/docs/editor/assets/audio) onto the stage. In doing so, an event is created with the preassigned asset. Alternatively, create a regular event by activating the event tool (`SHIFT + E`) and clicking on the stage. Once created, set the type setting in the inspector to Audio. Additional options to assign an asset and browse the Soundly library will be presented for audio events.
## Triggering an audio event
Like regular events, audio events can be triggered in a selection of different ways:
* **Timeline:** Whilst in animate mode, with a timeline selected, the event inspector will surface a button to key the event. Keying an event causes it to be reported. In the context of an audio event, reporting it will start the playback of the assigned audio asset.
* **Transitions:** Select a transition node within a State Machine and add an event via the inspector. You can choose whether the event should be reported at the start or at the end of the transition.
* **Listeners:** Select a listener within a State Machine and add a 'report event' action via the inspector. The pointer option will determine when the audio will play. For example, a pointer down listener with an assigned audio event targeting a shape will start playback when the user clicks on the shape.
### Monitoring audio
Audio levels can be monitored via the VU meter at the base of the inspector. Use the VU meter to check for clipping. This may occur if multiple audio events are playing at once, causing the overall output to clip. If you notice peak levels turning red, consider lowering the volume of your audio asset to provide more headroom.
# Events Overview
Source: https://rive.app/docs/editor/events/overview
Creating and signaling Rive Events
Events live within an artboard and are used to signal that something has happened. They can be fired from timelines, states, transitions, or listeners.
Rive supports three types of Events:
* [Open URL Event](/docs/editor/events/open-url-events) — Opens a URL at runtime
* [Audio Event](/docs/editor/events/audio-events) — Plays a sound
* [General Event (deprecated)](/docs/editor/events/general-events) — Previously used to communicate with runtime code
## Creating an Event
Use the Events tool located in the Toolbar and click anywhere on the artboard.

You'll notice that the Event is displayed on the artboard and in the Hierarchy.
Give your event a name so it’s easy to identify and reference.
You can rename it using the **Name** field, or by double-clicking the name directly on the artboard.

The Type dropdown allows you to change the Event type between Audio, URL, and General.

Each Event type has its own set of properties.
For more information on the specific event types, see [Open URL Events](/docs/editor/events/open-url-events), [Audio Events](/docs/editor/events/audio-events), and [General Events (deprecated)](/docs/editor/events/general-events).
If events aren't visible on the stage, open the **View Options** menu and make sure **Events** is enabled.
## Signaling an Event
We can signal an Event in four ways: from a timeline, a listener, a state, or a transition.
### Timeline
Signaling an Event from the timeline lets you control the exact moment in an animation when the Event fires.
First, select the timeline you want to add the Event to. Then use the **Report Event** button in the Inspector.

### Transition & State
You can report an event on a Transition or a State. To report an event, select the desired State or Transition and use the `+` button next to the Events section in the Inspector.

Now that we've selected the Event, we can decide whether it is signaled at the start or end of the Transition or State.
### Listeners
With your [Listener](/docs/editor/state-machine/listeners) selected, click the `+` below the State Machine Graph, and select **Report Event**.
# Exporting for Backup
Source: https://rive.app/docs/editor/exporting/exporting-for-backup
Exporting for backup is available on paid plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
Exporting a file for backup is useful when you want a hard copy of your file saved to your device, if you are looking to transfer a file from one user to another, or you're trying to send in a file for a support ticket. Unlike files exported for runtime (.riv), files exported for backup (.rev) contain all of the information that is typically stripped from the file.\
\
To export a file for backup, you can either do this from the file browser, or from the file menu when the file is open.
## Exporting from the file browser
To export a backup file from the file browser, first, find the file that you want to download.

\
Next, right click on the file and choose whether you want to download the file backup, or you can choose to download a specific revision of the file.
## Exporting from the file menu
To export a backup file, when the file is open, first, click on the file menu. Next, find the export option and select "for backup".

# Exporting for Runtime
Source: https://rive.app/docs/editor/exporting/exporting-for-runtime
Export a .riv file to use in apps, games, websites, and other supported runtimes.
Exporting for runtime is available on paid plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
A `.riv` file is the final Rive file you load at runtime. You can use the same exported file across supported runtimes, including web, mobile, game engines, and other platforms.
## Exporting a .riv File
To export a file for runtime, click the blue **Publish** button on the right side of the toolbar, or go to **Export** > **For runtime** from the left toolbar menu.
You can load the exported `.riv` file into your app, game, or website with any of Rive's [open source runtimes](/docs/runtimes/).
## Using newer files with older runtimes
Rive files are built for backwards compatibility. A file exported from the latest version of the editor can still load in older runtimes.
If a file uses a feature that was added after your runtime version was released, that specific feature may be skipped, while the rest of the file continues to run.
For full [feature support](/docs/feature-support), fixes, and performance improvements, keep your runtime up to date.
# Exporting for Video or Static Design
Source: https://rive.app/docs/editor/exporting/exporting-for-video-and-static-design
Exporting video and images is available on paid plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
Rive is all about interactive animation, but sometimes you need traditional formats such as MP4, GIF, or PNG sequence. Our Cloud Renderer turns any device into a supercomputer, allowing you to continue working while we generate your video or design file.
# Supported Formats
* H.264
* GIF
* PNG Sequence
* SVG Sequence
* WebM
* PNG
* SVG
# How to render
All rendering is done by creating Rendering Presets, then adding those Render Presets to the Render Que.
## Creating a preset
With the Artboard selected, you'll find the Render Presets option. Hitting the plus button will create a new Render Preset. This can be done from either [design or animate mode](/docs/editor/fundamentals/design-vs-animate-mode).
By creating a Render Preset in Design Mode you'll have the option to render either an SVG or PNG. This option can be changed using the dropdown menu with the preset selected.
By creating a Render Preset in [animate mode](/docs/editor/fundamentals/design-vs-animate-mode) you'll have the option to render a number of video formats including H.264, GIF, PNG/SVG sequence, and WebM. This option can be changed using the dropdown menu with the preset selected.

To the left of the Render Preset name, you'll find additional options which allow you to change a number of options.
* Animation
* Format
* FPS
* Duration
* Bit rate
* Size
## Adding a Preset to the Render Queue
When you're finished creating a preset, you'll need to add it to the Render Queue.
Do this by either using the Queue All button below the Render Presets, or by finding the Add to Render Queue option within a Render Preset.
Once you've added an item to the Render, you'll see that a new window appears. This window is the Render Queue. From here, you can change rendering options, as well as begin the rendering process.
You can always find the Renderer via the File Menu.\
\
To begin rendering an animation, use the play button next to the preset name, or hit the double play button at the top of the Render Queue.
Once you're file is finished rendering, you'll be able to download it from the Completed tab within the Cloud Renderer.
# Align and Distribute
Source: https://rive.app/docs/editor/fundamentals/align-and-distribute
Align objects to the artboard or to each other.
Use align and distribute tools to quickly position objects on the artboard or space multiple objects evenly.
When you select one or more objects, the align and distribute tools appear in the Inspector.
## Align
Align tools let you align objects to the left, right, top, bottom, or center.
When a single object is selected, the object aligns to the artboard. When multiple objects are selected, the selected objects align to each other.
## Distribute
Distribution tools appear when multiple objects are selected.
Use distribution to space selected objects evenly, either horizontally or vertically. This is useful when creating repeated elements, button groups, icon rows, or other layouts where objects should have consistent spacing.
# Artboards
Source: https://rive.app/docs/editor/fundamentals/artboards
Learn how to create, manage, and configure artboards in Rive.
Artboards are the foundation of your composition across both [Design and Animate mode](/docs/editor/fundamentals/design-vs-animate-mode). Each artboard defines a scene with its own dimensions, background, hierarchy, animations, and state machines.
Every Rive file contains at least one artboard, but you can create any number of artboards on the [Stage](/docs/editor/interface-overview/stage).
Artboards can also be converted to [components](/docs/editor/fundamentals/components), allowing you to create and control multiple instances of an artboard within your composition.
## Creating Artboards
### From a New Rive File
When you create a new Rive file, you'll be prompted to create an artboard. Choose a size from one of the presets, or enter a custom width and height. You can resize the artboard at any time.
The width and height define the artboard's dimensions in Rive. The size and aspect ratio at runtime may differ depending on the available space and your [layout settings](/docs/editor/layouts/layouts-overview).
Artboard dimensions are measured in units rather than pixels. For example, if a 500-unit-wide artboard is displayed at 1000 pixels wide, each unit represents 2 pixels. For simplicity, you can often treat these units like pixels while working in the editor.
### Creating Additional Artboards
A Rive file can contain multiple artboards.
Select the **Artboard Tool** or press **A**.
Click and drag anywhere on the stage to create an artboard.
Artboards can't be nested directly inside other artboards. To nest an artboard, first convert it to a [component](/docs/editor/fundamentals/components).
### Copying Artboards
Select an artboard and press **Cmd/Ctrl + D** to duplicate the artboard and all of its contents.
## Managing Artboards
Each artboard has its own hierarchy, animations, state machines, and properties.
### Hierarchy
Each artboard has its own hierarchy containing the objects that belong to that artboard. Select an artboard to view its hierarchy in the Hierarchy panel.
See [Hierarchy](/docs/editor/interface-overview/hierarchy) for more information about organizing and navigating objects.
### Animations & State Machines
Animations and state machines belong to individual artboards. Select an artboard to view and edit its animations and state machines.
See [Animations](/docs/editor/animate-mode/animate-mode-overview) and [State Machines](/docs/editor/state-machine/state-machine) to learn more.
## Artboard Properties
Select an artboard to view and edit its properties in the Inspector.
* **Transform** - Set the artboard's position, size, scale, rotation, and origin.
* **Layout** - Control how the artboard responds when its size changes. See [Layout](/docs/editor/layouts/layouts-overview).
* **Fill** - Set the artboard's background color.
* **Model** - Choose the [view model](/docs/editor/data-binding/binding-data) associated with the artboard.
* [Clip](#clip) - Control whether content outside the artboard's bounds is visible.
### Clip
Enabling the **Clip** toggle hides content that extends beyond the bounds of the artboard.
Clipping can be expensive to render, so only enable it when needed.
In most cases, you don't need to enable **Clip** on a top-level artboard because content outside its bounds isn't rendered at runtime.
## Default Artboard & State Machine
The default artboard and state machine are used at runtime when an artboard or state machine isn't explicitly specified.
To change the default, use the dropdown to select the artboard and state machine you want to use.
# Assets
Source: https://rive.app/docs/editor/fundamentals/assets-overview
Assets are reusable resources in a Rive file. Some assets are imported from external files, such as images, fonts, audio, and custom blobs. Others are created in Rive, such as scripts and components.
This page focuses on imported file assets. For these assets, you can control whether the asset is included in the exported .riv file, loaded separately at runtime, or excluded from the export.
For other asset types, see [Scripts](/docs/scripting/getting-started) and [Components](/docs/editor/fundamentals/components).
## Assets Panel
Use the Assets panel to find, manage, and create assets in your file. The panel includes imported file assets, such as images, fonts, audio, and blobs, along with assets created in Rive, such as scripts and components.
Use the panel to import new assets, create Rive assets, sort and filter by type, and select an asset to view its settings in the Inspector.
## Asset Types
Imported file assets include:
* **Raster images:** JPEG, PNG, WebP
* [Photoshop files](/docs/editor/assets/psd): PSD
* [SVG files](/docs/editor/assets/svg): Vector artwork imported as editable Rive content
* [Fonts](/docs/editor/text/fonts): TTF, OTF
* [Audio](/docs/editor/assets/audio): MP3, WAV, FLAC
* [Lottie](/docs/editor/assets/lottie): `.lottie` files, available on Enterprise plans
* **Blob assets:** Advanced custom assets
Blob assets are useful for advanced workflows where a script needs access to a custom file type, such as a 3D model.
## Importing Assets
### Drag and Drop
You can import assets by dragging files into Rive or by using the Assets panel.
If the file type is not directly supported, Rive asks if you want to import it as a custom blob asset.
### Browse Files
To import from the Assets panel, click **+**, then choose the asset you want to import.
### Asset-specific workflows
* [SVG Files](/docs/editor/assets/svg) for importing SVGs from Figma, Illustrator, and other vector tools.
* [Photoshop Files](/docs/editor/assets/psd) for importing layered PSD files.
* [Font Files](/docs/editor/text/fonts) for adding custom and Google Fonts.
* [Audio Assets](/docs/editor/assets/audio) for browsing sounds, importing audio, and creating clips.
## Asset Settings
Select an asset in the Assets panel to view its settings in the Inspector. The available settings depend on the asset type.
Asset settings panels can include:
* [Information](#information): Details about the selected asset.
* [Compression](#compression): Image compression settings for raster images and PSD files.
* [Export Options](#export-options): Settings that control how the asset is handled when exporting a `.riv` file.
### Information
The **Information** panel shows details about the selected asset, such as file size, dimensions, usage count, and creation date.
Use **Replace** to swap the source asset without changing how the asset is used in the Rive file. For example, replacing an image updates the asset everywhere it is used.
The information shown depends on the asset type:
* **Fonts** can show family, style, supported scripts, publisher/designer information, license details, and glyphs.
* **Audio** can show volume, format, channels, sample rate, duration, and an audio preview.
### Compression
Raster images and PSD files include a **Compression** panel. Use compression settings to control how the asset is encoded in the exported `.riv` file.
Choose a compression format, then adjust the **Quality** value to balance image quality and file size. WebP is recommended for most raster images because it usually provides smaller file sizes while preserving good visual quality.
### Export Options
Export options control how an imported asset is handled when you export a .riv file for runtime. Use these settings to decide whether an asset is included in the .riv file, loaded separately at runtime, or excluded from the export.
The available export options depend on the asset type.
#### Behavior
**Behavior** controls whether the asset is exported.
* **Automatic**: Export the asset if it is used in the file.
* **Force Export**: Export the asset even if it is not currently used in the file.
* **Prevent Export**: Do not export the asset, even if it is used in the file.
SVG assets only use Automatic because SVGs are converted to Rive vector objects rather than exported as separate file assets.
Use **Prevent Export** when the asset is only a placeholder in the Rive file and will be replaced at runtime. For example, a profile card might use a placeholder avatar in the editor, while your app loads the real user's avatar at runtime.
Use **Force Export** when an asset may not be visible or directly used in the editor, but still needs to be available at runtime. For example, a file might show one image in the editor, while your app randomly selects from a set of images at runtime.
#### Type
Type controls how the asset is included or referenced by the exported .riv file. This option is available for all imported asset types except SVGs.
* **Embedded**: Include the asset data directly in the .riv file.
* **Referenced**: Store a reference to the asset so it can be loaded separately at runtime.
* **Hosted**: Load the asset from Rive’s servers instead of embedding it in the `.riv` file. Hosted assets are available on paid Voyager plans.
#### Source
See [Audio Assets](/docs/editor/assets/audio#export-settings) for audio-specific export settings, including Source.
#### Include
See [Font Assets](/docs/editor/text/fonts#export-settings) for font-specific export settings, like Include.
# Components (formerly Nested Artboards)
Source: https://rive.app/docs/editor/fundamentals/components
Components streamline your workflow with reusable artboards and animations. Changes made to the source component are reflected across all of its instances.
## Creating a Component
Any artboard can be converted to a component. To do so, select an artboard on the stage and use the component icon in the inspector to toggle its status.
Alternatively, you can use the `Shift` + `N` shortcut with an artboard selected. If you're coming from Figma, then the `Cmd/Ctrl` + `Alt/Option` + `K`, shortcut will also work.
Select the component toggle in the inspector again to revert your selection back to a regular artboard, or use the `Shift` + `Alt/Option` + `N` shortcut.
Currently, only artboards that have been flagged as components will be exported to your `.riv` file. If you think you may want to programmatically access an artboard at runtime, you should mark it as a component. More options on specific export behaviors are coming soon.
## Using Components
Use the Component Tool — formerly known as the Nested Artboard Tool — to select and place instances of a component on the stage. Select the tool from the toolbar or use the `N` shortcut to enable it.
Click anywhere on the stage to place the component in the desired location. A menu will display available components to instance. If none show up, you may have no artboards marked as components in your file.
Alternatively, select the dropdown menu to the right of the toolbar icon to select the component ahead of time. The menu is informed by the sort mode of the assets tab — the 'Custom' mode will present components as they’re organized in the asset panel, while the 'Source/Type' mode will present components from their source. The latter will become useful with our Libraries feature.
## Configuring a Component Instance
Once you’ve added an instance of a component, select a timeline or state machine for playback.
### State Machines
After assigning an instance, the default state machine is displayed in the inspector.
### Adding an Animation
You can playback any animation associated with a component. You’ll need to add the desired animation to the instance using the plus button in the Inspector.
These animations can be used by themselves, mixed with the state machine, or layered with other animations.
Note that before adding the animation, you must select whether it's a simple or remapped animation.
#### Simple
Simple animations are an easy way to playback a component's timeline.
A simple animation lets you key its start point on a timeline. You also have the option to change the animation's playback speed.
#### Remap
Remap animations allow you to key time values of an animation on the timeline. This lets you stretch, shrink, or even play an animation in reverse.
Note that the time value is in percent, with 0% representing the start of the timeline and 100% representing the end.
### Mix Value
As you add additional animations to a Component, animations begin to mix together. This mixing is important, especially when multiple animations share keyed properties. Without adjusting this value, your Component may not playback your animations in the way you want.
By default, any animation added to a component starts with a mix value of 100%. You can adjust this value in design mode or in a specific animation by setting keys. **Note that an animation that has a non-zero mix value will always be mixing with other animations, regardless if it has a play key set or not.**
To ensure the correct animation is playing, ensure that you key the mix value for the desired animation to 100%, and all other animations have a mix of 0%.
### Mode (Component Sizing)
Component instances can use **Node**, **Leaf**, or **Layout** sizing to control how they respond to the space available from their parent.
See [Component Sizing](/docs/editor/layouts/component-sizing) to learn how each option scales, fits, or reflows a component.
# Edit Vertices
Source: https://rive.app/docs/editor/fundamentals/edit-vertices
No matter the type of vector you create, you can edit the vertices by changing their position or handles in both design and animate mode.
## Edit Vertices mode
To enter Edit Vertices mode, either select the shape and hit enter twice or select a path and hit enter once.
After activating Edit Vertices mode, you can select any vertex, reposition it, and edit the bezier handles.
Use [Deep Select](/docs/editor/interface-overview/selection-and-navigation#deep-select) to select a specific path inside nested groups on the stage.
### Path Options
Each path in Edit Vertices mode has a set of path options at the top of the Inspector.
**Done Editing Button**
The Done Editing button can be used to exit Edit Vertices Mode.
**Open Path**
The Open Path button will disconnect the last vertex from the first vertex.
**Reverse Direction**
The Reverse Direction button can be used to reverse the direction of the path. Depending on the Fill-Rule, this can eliminate holes in our shape by changing the mathematical value of the selected path.
**Convert Radial Corners**
Straight vertices with a corner radius will deform when the scale transform is applied to the shape or path layer. You can convert radial corners from a procedural property to a defined set of vertices. This process will eliminate deformation of the corner.
## Bezier Handles
**Straight**
The default handles are set to straight, which creates straight edges between vertices.
**Corner Radius**
The Corner Radius property allows you to round straight corners. This property only appears on vertices that are set to straight.
**Mirrored**
Mirrored is the default handle when you create a vertex by clicking and dragging. These handles always keep the same rotation and length.
**Detached**
Detached handles allow each handle to have its own rotation and length.
**Asymmetric**
Asymmetric handles share the same rotation but can have lengths independently of each other.
# Fill and Stroke
Source: https://rive.app/docs/editor/fundamentals/fill-and-stroke
The Fill and Stroke section of the Inspector allows you to add and modify the Fill and Stroke properties of the currently selected object. You can create as many fills or strokes as you'd like.
# Fill
### **Create a new Fill**
To create a fill, select a shape, then use the plus button under the Fill and Stroke section of the Inspector. Be sure to select Fill from the new menu. You'll be able to tell that a layer is a fill by looking at the color box on the left side.
### **Changing Fill color**
To change the color of a Fill, select the color box on the left side of the Fill layer. This will open the Color Picker. From there, you can use the various sliders to choose which color you'd like for the Fill.
### **Changing Fill Type**
When a new shape is created, by default the shape will have a solid fill. When a new fill is added, by default the fill type is set to linear. We often need to change the fill type between the different types. This can be done by selecting the color box.
Once the Fill has been opened, you'll find the Fill Selector dropdown in the top of the option box.
The different fills that can be selected are:
* **Solid**
* **Linear Gradient**
* **Radial Gradient**
### **Changing Fill color (Gradient)**
To change the color of a Fill, select the color box on the left side of the Fill layer. This will open the Color Picker.
When a gradient is selected, you'll notice a new bar appear above the color picker. This represents the color of the gradient at different points.
By default, a gradient has two points.
### **Changing the color of a stopper**
To change the color of a particular color stopper, start by selecting the stopper you'd like to change.
Next, use the various sliders to choose which color that stopper should be.
### **Adding and removing stoppers**
To add a new color stopper, click any space along the long that isn't currently occupied by another stopper. This will generate an additional color stopper.
To delete a color stopper, select the stopper you'd like to delete, then hit the Delete or Backspace key.
### **Change Fill Order**
The order of fills determines their render order, with fills on top rendered in front and fills at the bottom rendered in back.
This order can be changed at any time by clicking and dragging on an empty area within the layer.
### **Fill Properties**
Each Fill has its own properties which can be edited and keyed on the timeline. Some of these properties can be found by using the fill option button.
**Fill Name -** You can edit the name of a fill using this property.
**Blend -** This option can be used to change the Blend Mode of an individual Fill. By default, this mode will be set to inherit, which inherits the blend mode from the shape layer.
**Fill Rule -** This option can be used to change the fill rule for the Fill. This must be set to clock-wise if you want the fill to be feathered.
\*\*Feather - \*\*This option can be toggled to feather the chosen Fill. Read more about Feathering below.
### **Deleting and hiding a Fill**
Often times we'll need to delete or hide a particular Fill. This can be done by selecting the shape, then using the eye icon to hide the Fill, or the minus icon to delete the fill.
### Fill Rule
The Fill Rule determines how overlapping paths in a shape will be filled:
* **Non-Zero** assigns a +1 value to clockwise paths and a -1 value to counter clock wise paths. Areas that equal a value other than 0 will be filled.
* **Even-Odd** assigns a +1 value to clockwise paths and a -1 value to counter clock wise paths. Areas that equal an even value will be filled while odd values wont be.
* **Clockwise** a Fill Rule exclusive to Rive. This fill rule enables manual subtraction of paths which can be found in edit vertices mode. This fill rule is also required for shapes where you'd like to enable vector feathering.
# Stroke
### Create a new Stroke
To create a Stroke, select a shape, then use the plus button under the Fill and Stroke section of the Inspector. Be sure to select Stroke from the new menu. You'll be able to tell that a layer is a Stroke by looking at the color box on the left side. Strokes are represented by an outlined box.
### **Changing stroke color (solid)**
To change the color of a Stroke, select the color box on the left side of the Stroke layer. This will open the Color Picker. From there, you can use the various sliders to choose which color you'd like for the Stroke.
### **Changing Stroke type**
By default, strokes are set to a solid color, but various stroke types are available from the Color Picker menu.
The different strokes that can be selected are:
* **Solid**
* **Linear Gradient**
* **Radial Gradient**
### **Changing Stroke color (Gradient)**
To change the color of a Stroke, select the color box on the left side of the Stroke layer. This will open the Color Picker.
When a gradient is selected, you'll notice a new bar appear above the color picker. This represents the color of the gradient at different points.
By default, a gradient has two points.
### **Changing the color of a stopper**
To change the color of a particular color stopper, start by selecting the stopper you'd like to change.
Next, use the various sliders to choose which color that stopper should be.
### **Adding and removing stoppers**
To add a new color stopper, click any space along the long that isn't currently occupied by another stopper. This will generate an additional color stopper.
To delete a color stopper, select the stopper you'd like to delete, then hit the Delete or Backspace key.
### **Deleting and hiding a Stroke**
Often times we'll need to delete or hide a particular Stroke. This can be done by selecting the shape, then using the eye icon to hide the Stroke, or the minus icon to delete the Stroke.
# Stroke Properties
Each Stroke has its own properties which can be edited and keyed on the timeline. Some of these properties can be found by using the Stroke option button.
**Stroke Name -** You can edit the name of a Stroke using this property.
**Blend -** This option can be used to change the Blend Mode of an individual Stroke. By default, this mode will be set to inherit, which inherits the blend mode from the shape layer.
**Cap -** This option changes the end cap of a Stroke. Read more about the different Caps below.
* **Butt** The end of the stroke is a straight line and does not extend beyond the end vertices. On a zero-length path, the stroke will not be rendered at all.
* **Round** The ends of a stroke are rounded. On a zero-length path, the stroke is a full circle.
* **Square** The ends of a stroke are squared off and extend beyond the end vertices. On a zero-length path, the stroke is a square.
**Join -** This option changes how the corners of a Stroke are rendered. Read more about the different Join options below.
* **Round** creates a rounded corner.
* **Bevel** creates a beveled corner.
* **Miter** creates a mitered corner.
**Apply Transformations -** The Apply Transformations toggle determines whether the shape layers scale will affect the thickness of the stroke. When this is toggled off, the thickness of the stroke will stay the same regardless of scale.
**Feather -** This option can be toggled to feather the chosen stroke. Read more about Feathering below.
**Stroke Type** - At the bottom of the Stroke Options Panel, you'll find options to change your stroke between a solid, trim, dashed stroke.
* **Solid -** Renders the stroke as a solid stroke. This is the default stroke type for each new stroke created.
* **Trim -** Lets you animate the start, end, and offset of a line segment. Read more [here](/docs/editor/manipulating-shapes/trim-path).
* **Dashed -** Lets you create dashed strokes with animatable property like the length of the dashed segment and offset. Read more [here.](/docs/editor/manipulating-shapes/trim-path)
# Vector Feathering
Vector feathering is a new way to feather both Fills and Strokes. Vector Feathering is a technique we invented at Rive that can soften the edge of vector paths without the typical performance impact of traditional blur effects.
### **Enabling Vector Feathering**
There are two main ways to enable vector feathering on any Stroke of Fill.
* **Feather Icon -** The feathering icon can be used on any Fill or Stroke layer to enable vector feathering.
* **Feather Toggle -** The feather toggle can be found in the Fill / Stroke options panel.
### Feathering Options
Feathers can be customized in a number of ways. The Feathering options can be found in the options panel once Feathering has been enabled on a Fill or Stroke.
**Direction** - This option lets you choose which direction the path will feather as you increase the feather amount.
* Outer - This option creates a feather that will feather outward from the path.
* Inner - This option creates a feather that will feather inward from the path.
**Amount** - This option lets you increase or decrease the amount of feather applied.
**Space -** Determines how the feathered fill or stroke will apply transforms from the parent if any offset is present on the feather.
* World - Transforms will be applied from the world transform. Feather will now act as a drop shadow.
* Local - Transforms will be applied from the local transform. This mode will have the feather work with transforms as you'd expect.
**Offset** - The Offset properties let you move the feather away from the path by increasing or decreasing the X and Y numbers.
# Effect Groups
Effect groups let you apply a single path effect — or a stack of effects — to multiple strokes and fills at once, without having to configure each one individually. This is useful any time you have multiple shapes that need to share the same trim, dash, or scripted effect behavior, and you want to control them from one place.
# Freeze and Origin
Source: https://rive.app/docs/editor/fundamentals/freeze-and-origin
When you transform objects, their children inherit the same transformations. The location where these transformations happen (sometimes called the origin, anchor point, or pivot) affects how your objects animate.
For example, manipulating the scale of a group creates different results if the scale originates in the center or the bottom.
You need to reposition the parent group to change the point of origin for these transformations. However, moving a parent causes all the children to move with it. The Freeze feature makes it possible to achieve this without having to rework the hierarchy structure.
# Origin of a Procedural Path
Procedural objects (like artboards and procedural paths) have an origin property. The origin of a procedural path determines where its properties originate from. For example, changing the width of a rectangle with its origin in the middle (50% X and 50% Y) causes it to grow from its center.
Changing the width on a rectangle with its origin on the left side (0% X) causes it to grow from its left.
This is particularly useful when animating paths that have other procedural properties enabled, such as rounded corners.
You can use the Freeze feature to change the Origin position on the Stage. Alternatively, set the exact value in the Inspector.
# Origin of a Custom Path and Group
### Freeze Mode
The Freeze feature allows you to move any parent object (groups, shapes, bones) without affecting the position of its children. Activate Freeze in the [Transform Tools menu](/docs/editor/interface-overview/toolbar) or use the `Y` shortcut.
When Freeze Mode is active, you'll notice that your Stage is wrapped in a blue outline. You're now free to move the Origin without affecting the children.
Be sure to turn off Freeze by pressing `Y` again.
# Changing Origin with Align Tools
You can quickly change the location of an origin with the align tools.
Start by selecting the shape, then holding Option on Mac, or Alt on Windows. \
\
This now allows the align tool to reposition the Origin various positions.
# Groups
Source: https://rive.app/docs/editor/fundamentals/groups
Use groups to organize your graphics or to add extra transform spaces.
Activate the Group tool with the `G` shortcut. Click anywhere in an artboard to add a new group. Now drag and drop objects into the group in the Hierarchy.
You can also wrap a selection of shapes into a group with `⌘`+`G` in macOS or `Ctrl`+`G` in Windows.
Unwrap a group with `⌘`+`Shift`+`G` in macOS or `Ctrl`+`Shift` +`G` in Windows.
## Group Style
The Style property of a group can be set to Group or Target.
### Group
Group is the default behavior, which behaves as described in the [Selection and Navigation](/docs/editor/interface-overview/selection-and-navigation).
### Target
The Target option draws an icon on the stage that is visible regardless of whether the group has children (usually a group only displays an icon if it is empty). Target visibility can be toggled on or off in the View Options menu.
Targets are directly selectable on the stage. You don't need to use [Deep Select](/docs/editor/interface-overview/selection-and-navigation#deep-select) to select them.
The Target option is particularly useful when working with Constraints.
If targets aren't visible on the stage, open the **View Options** menu and make sure **Targets** is enabled.
} href="/editor/constraints/">
Constraints are a way to control the properties of an object through another target object. Some constraints can set limits on these properties (and their hierarchical relationships), while others can copy properties from one object to another.
# Pen Tool Overview
Source: https://rive.app/docs/editor/fundamentals/pen-tool-overview
The Pen tool allows you to create custom vector paths as well as add additional vertices to your procedural paths. Learn more about the Pen tool by either watching the video or reading more below.
## Creating custom shapes
The Pen tool allows you to create custom vector shapes. Activate the Pen tool by finding it under the Create Tools menu or by using the `P` shortcut.
Click on the stage to place vertices.
Click and drag to create a vertex with bezier handles. When you are finished, hit `esc` on your keyboard.
## Path & vertex shortcuts
* Hold Alt (Opt) to detach the Pen tool while drawing a path.
* Ctrl+click (Cmd+click) on the vertex to toggle between mirrored and straight handles.
* With Select or Pen tool, Ctrl+click (Cmd+click) on the vertex handle to remove that handle.
* With Select or Pen tool, Alt+click (Opt+click) on a vertex handle to detach the handle.
* With Pen tool, Alt+click (Opt+click) on a vertex to delete the vertex.
* Alt+drag (Option+drag) to duplicate vertices.
# Shape Tools
Source: https://rive.app/docs/editor/fundamentals/procedural-shapes
Create editable shapes with adjustable properties.
Shape tools let you create common shapes, such as rectangles, ellipses, polygons, and stars. These shapes keep editable properties, such as width, height, corner radius, and number of points, until you convert them to custom paths.
## Creating a Shape
Shape tools are available from the **Create Tools** menu.
Open the **Create Tools** menu and select a shape tool.
Click and drag inside an artboard to draw the shape.
Hold `Shift` while dragging to constrain the shape's proportions.
See [Freeze and Origin](/docs/editor/fundamentals/freeze-and-origin) to learn how to change the center point of a shape.
## Shape Properties
You can update a shape's properties in the inspector. Available properties depend on the selected shape type.
* **Size**: The width and height of the shape.
* **Origin**: The [origin](/docs/editor/fundamentals/freeze-and-origin) point of the shape.
* [Corner](#corner): The corner radius of rectangles.
* [Radius](#radius): The radius of polygons and stars.
* [Points](#points): The number of points on polygons and stars.
* [Angle](#angle): The angle between the outer and inner points of a star.
### Corner
Adjust the corner radius by dragging the circular handles on each corner of the rectangle.
Hold `Alt` (Windows) or `Option` (macOS) while dragging to adjust a single corner independently.
### Radius
Drag the radius handle to adjust the radius of all points on the shape at once.
### Points
Control the number of points by dragging the points handle up or down.
### Angle
Drag the handle on the inner point up or down to adjust the angle on the inner and outer points of a star.
## Converting a Shape to a Custom Path
Press `Enter` to convert the selected shape to a path. After conversion, you can edit each vertex directly.
Converting a shape to a path removes its procedural properties, such as width, height, corner radius, and number of points. Any animations applied to those properties are also removed.
To edit a path, see [Edit Vertices](/docs/editor/fundamentals/edit-vertices).
# Revision History
Source: https://rive.app/docs/editor/fundamentals/revision-history
Rive saves your files automatically as you work. Even if multiple people are working on the same file at the same time, Rive tracks all changes and stores them in the Revision History.
## View a file's history
The Revision History can be accessed from the [Editor Menu](/docs/editor/interface-overview/toolbar).
## Restore a revision
Select a revision to preview it and press the Edit Current Revision button. This copies the selected revision and creates a new entry at the top of the list. This guarantees that even restoring revisions is non-destructive, and you can always go back to the previous version of the file.
## Save a Revision
From the Editor Menu, select the Create a Revision button. You'll then be prompted to name the revision.
Once the revision is created, you can restore that revision using the steps above.
# Shapes and Paths Overview
Source: https://rive.app/docs/editor/fundamentals/shapes-and-paths-overview
Rive allows you to create, edit, and animate vector graphics using either procedural or custom shapes. These graphics combine shape and path layers to define them, which Rive exposes to give you greater flexibility and control with your designs and animations.
To learn more about Shape and Path layers, watch our video on Shapes and Paths, or read more below.
## Shape layer
Vectors in Rive are rendered on shape layers. Shape layers define the style of the shape by allowing you to customize the fill and stroke.\`
## Path layer
The actual shape of a vector is defined by a path (or multiple paths). Expanding a shape layer in Rive will reveal the paths it's using.
You can add new paths to any shape by dragging and dropping an existing path onto the desired shape layer.
### Path layer properties
Path layers display properties that to the type of path. Learn more about [Procedural Shapes](/docs/editor/fundamentals/procedural-shapes).
## Enter and Esc shortcuts
Use the `Enter` key to quickly navigate down the Hierarchy. If you have a shape selected, this allows you to select the child path layer quickly.
Use the `Esc` key to quickly navigate up the Hierarchy. If you have a path selected, this allows you to select the parent shape layer quickly.
# Transform Spaces
Source: https://rive.app/docs/editor/fundamentals/transform-spaces
Container Objects, like Groups, Bones, and Layouts, allow you to create new transform spaces for your graphics, opening up the ability to animate graphics from multiple areas of interest. For example, you might want a planet to rotate on its own axis while also rotating around another planet. Multiple transform spaces (achieved by nesting groups, bones, and other container objects) allow you to achieve this.
This technique is a fundamental concept for all motion graphics. To learn more about transform spaces, be sure to watch our video on hierarchical relationships.
## Transform space example

Nest multiple groups to transform your shapes from different locations. In the example above, a group is rotating the Earth, another is rotating the Moon around the Earth, and another is rotating the moon on its axis.

# Using the Rive Editor
Source: https://rive.app/docs/editor/get-rive
Choose how you want to use the Rive editor: in your browser or as a desktop app.
To get started, [sign up for a Rive account](https://rive.app/signup/?redirect=%2Faccount%2Fteams%2F\&utm_source=docs\&utm_medium=content).
You can use the Rive editor as a **desktop app** or directly in your [browser](https://editor.rive.app). Both versions provide the same core features and workflows.
Download the Rive editor for macOS or Windows.
Voyager users can preview upcoming features in the Early Access Editor.
# Debug Panel
Source: https://rive.app/docs/editor/interface-overview/debug-panel
Inspect logs, problems, AI changes, tests, and audio while working in Rive.
The Debug Panel shows output and diagnostic information while you work in Rive. Use it to inspect console logs, find problems, review AI Agent changes, run tests, and preview audio.
## Tabs
The Debug Panel is organized into tabs.
* **Console** - view script logs, runtime errors, and State Machine activity.
* **Problems** - view code issues, broken bindings, validation warnings, and other problems in the file.
* **Changes** - review pending changes from the AI Agent.
* **Testing** - run [test scripts](/docs/scripting/debugging/unit-testing).
* **Audio** - [preview audio assets](/docs/editor/assets/audio#previewing-sounds) and [create clips](/docs/editor/assets/audio#creating-clips).
### Console
The Console shows output from scripts and State Machines.
Use the sidebar in the Console tab to switch between the script console and the State Machine console.
Use the Console options to clear the console, clear the console when playback starts, or copy console output.
#### Script Console
The script console shows output from scripts, including `print()` messages and runtime errors.
See [Script Console](/docs/scripting/debugging/debug-panel#console) for more information.
#### State Machine Console
The State Machine console shows State Machine activity during playback, such as entering a state or taking a transition.
### Problems
The Problems tab lists issues found in the file. These can include code problems, broken data bindings, validation warnings, or problematic State Machine setup.
Errors are indicated by a red dot, while warnings are yellow.
Use the Problems options to filter problems, show or hide warnings, and include non-script validation.
Click a problem to open the affected code line, binding, State Machine item, or related editor location.
See [Script Problems](/docs/editor/interface-overview/debug-panel#problems) for handling script-related problems.
### Changes
The Changes tab shows pending changes from the AI Agent.
Use this tab to review changes before applying them to your file.
# File Browser
Source: https://rive.app/docs/editor/interface-overview/file-browser
Create, organize, and manage files, projects, and workspaces.
The File Browser is where you create, open, organize, and manage your Rive files. You can also browse recent files, open Marketplace examples, switch between workspaces, and manage projects.
## Home
The Home view shows your recent files, links to tutorial videos, and Marketplace examples.
## Managing Files
Right-click anywhere within the File Browser to create a new file or folder.
Right-click a file to open additional options, including:
* **Open**, **Copy**, **Cut**, **Duplicate**, and **Delete** - common file actions
* **Show File in Browser** - open the file’s location in the File Browser
* **Download** - download the file as a `.riv`
* **Download Backup** - download a `.rev` backup file
* **Download Revision** - download a specific revision
## Toolbar
The toolbar includes options for managing your account, changing display options, creating folders, and creating new files.
## Sidebar
Use the sidebar to search files, view recent files, access files shared with you, and navigate between workspaces and projects.
### Projects
Projects help you organize files within a workspace.
Use projects to group related files, such as files for a client, feature, campaign, or product area. For example, you might have separate projects for personal work and freelance work.
Projects and folders are available on Cadet, Voyager, and Enterprise plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
#### Create a New Project
In the sidebar, click **+ Create Project**.
Add a project name and choose who should have access.
#### Managing Project Members
You can invite people to a specific project. This is useful when someone needs access to a set of files, but does not need access to the entire workspace.
In the sidebar, select a project.
Expand the project details panel to invite, remove, or change permissions for individual members or groups.
### Workspaces
Workspaces are separate spaces for files, members, and billing. You might have a personal workspace, a company workspace, or separate workspaces for different teams.
See [Workspaces Overview](/docs/account-admin/workspaces/workspaces-overview) for more information.
If you only want to organize files or share a specific set of files, create a project instead of a new workspace.
# Hierarchy
Source: https://rive.app/docs/editor/interface-overview/hierarchy
View, organize, and reorder objects in your file.
The Hierarchy is a tree view that shows the objects on the stage. It shows both how objects are nested and the order in which they are rendered.
## Renaming Items
You can rename items in the Hierarchy to make your file easier to understand and navigate.
To rename an item, double-click its name in the Hierarchy, enter a new name, then press Enter.
## Parent-Child Relationships
Each row in the Hierarchy represents an item on the stage. Items with children have a button with an arrow next to them. Use this button to expand or collapse the list of children.
Any object can be a parent or child of another object. When an object is nested under another object, it inherits the transformations of its parent.
For example, scaling a parent object also scales its children. Parent transformations are applied from the parent’s origin, not the child’s local origin.
You can nest objects as deeply as needed, creating children, grandchildren, great-grandchildren, and so on.
To change the relationship between objects, drag an item onto another item to make it a child. Drag it out of the parent to remove the relationship.
## Draw Order
The Hierarchy also controls draw order. Objects higher in the Hierarchy render above objects lower in the Hierarchy.
You can override the default draw order with [custom draw order](/docs/editor/animate-mode/animating-draw-order).
To change draw order, drag objects up or down in the Hierarchy.
## Lock, Isolate, and Hide Items
When you hover over an item in the Hierarchy, controls appear for locking, isolating, or hiding that item.
* **Lock** prevents the item from being selected or edited on the stage.
* **Isolate** focuses on the item by temporarily hiding other items from view.
* **Hide** temporarily hides the item.
These controls also apply to any children nested under the item.
Lock and isolate are editor workflow tools. They do not change the exported file or runtime behavior.
## Context Menu
Right-click an item in the Hierarchy to open additional options. Depending on the selected item, options may include:
* Copy, cut, paste, delete, copy styles, and paste styles
* Expand or collapse nested items
* Show dependencies
* Lock, hide, or isolate elements
* Add a [tag](/docs/editor/tagging)
* Reverse the order of elements
* [Export name](/docs/editor/exporting/exporting-names)
Many of these options are also covered in more detail on related pages.
# Inspector
Source: https://rive.app/docs/editor/interface-overview/inspector
View and edit properties for the current selection.
The Inspector is the right sidebar in the editor. It shows the available properties and options for the current selection.
## Selection Properties
When you select an element, the Inspector updates to show controls for that element.
For example, selecting an artboard, object, joystick, script, property group, or State Machine item shows different options based on what that element can do.
## File Properties
When nothing is selected, the Inspector shows file-level options.
These include:
* [Stage background colors](/docs/editor/interface-overview/stage#stage-background-colors) for Design and Animate modes
* Scripting optimization and debug settings
* [Shader targets](/docs/scripting/wgsl-shaders#shader-targets)
* [Tags](/docs/editor/tagging)
* [Default interpolation](/docs/editor/animate-mode/interpolation-easing#default-interpolation)
# Interface Overview
Source: https://rive.app/docs/editor/interface-overview/overview
Rive's interface only shows what is needed when you need it. It is divided into a few main panels, which are described below.
## File Browser
The File Browser is where you create, open, organize, and manage your Rive files. You can also browse recent files, open Marketplace examples, switch between workspaces, and manage projects.
Read more on the [File Browser page](/docs/editor/interface-overview/file-browser).
## Toolbar
The Toolbar displays the tools you have available to create, rig, and manipulate items on the stage. In addition to these tools, the Toolbar houses a variety of options to allow you to customize the look of your file, set your main artboard, and export or share your file.
Read more on the [Toolbar page](/docs/editor/interface-overview/toolbar).
## Sidebar
The sidebar is the left side of the editor. It contains panels for navigating your file, managing assets, working with data, editing animations, and using the AI Agent.
Read more on the [Sidebar page](/docs/editor/interface-overview/sidebar).
## Inspector
The Inspector is the right sidebar in the editor. It shows the available properties and options for the current selection.
Read more on the [Inspector page](/docs/editor/interface-overview/inspector).
## Viewport
The Viewport is the central area between the Toolbar, Sidebar, and Inspector. It can display the Stage, scripts, and other panels.
The Stage is where you create and edit artboards, which contain the designs and animations in your Rive file.
Read more on the [Viewport page](/docs/editor/interface-overview/stage).
## Timeline
The Timeline surfaces from the bottom of the screen upon entering [animate mode](/docs/editor/fundamentals/design-vs-animate-mode). Here you can create new states, access playback controls, settings, and set keyframes for object parameters. Select a timeline from the left-hand list to switch between the respective timelines.
Read more on the [Timeline page](/docs/editor/fundamentals/design-vs-animate-mode).
## State Machine graph
When a state machine is selected, the Timeline is replaced by a graph. This is where you'll be working on the state machine.
Read more on the [State Machine page](/docs/editor/state-machine/state-machine).
## Debug Panel
# Selection and Navigation
Source: https://rive.app/docs/editor/interface-overview/selection-and-navigation
Select objects, move through nested groups, and navigate the stage.
Use selection and navigation controls to choose objects, move through nested groups, and move around the stage.
For a complete list of shortcuts, see [Keyboard Shortcuts](/docs/editor/keyboard-shortcuts).
## Selecting Objects
Click an object to select it. To select multiple objects, drag over them to create a marquee selection.
Hold `Shift` while clicking or dragging to add objects to the current selection or remove selected objects.
### Select Objects Inside Groups
To select an object inside a group, double-click the object. This moves you one level down in the hierarchy, so you can select objects inside that group.
### Deep Select
To select an object inside nested groups, hold `⌘` on macOS or `Ctrl` on Windows, then click the object.
Deep select lets you select an object directly, even when it is inside multiple groups.
[Targets](/docs/editor/fundamentals/groups#target) are directly selectable on the stage. You don't need to use **Deep Select** to select them.
### Select Behind
When you hover over an object on the stage, Rive outlines the object that will be selected if you click.
If multiple objects overlap, press `Alt` to cycle through objects under the cursor. When the object you want is outlined, click to select it.
### Moving Up and Down the Hierarchy
Use `Enter` and `Esc` to move through nested objects in the hierarchy.
* Press `Enter` to move down into the selected group and select its first child.
* Press `Esc` to move up and select the parent of the current selection.
## Navigating the Stage
### Pan
To pan the stage, right-click and drag.
You can also hold `Spacebar` to temporarily switch to the Pan tool, then click and drag to move around the stage.
If you're using a trackpad, scroll left or right to pan horizontally.
### Zoom
To zoom in or out, place your cursor over the stage, then hold `⌘` on macOS or `Ctrl` on Windows and scroll.
You can also use keyboard shortcuts:
* Press `+` to zoom in.
* Press `-` to zoom out.
* Press `⌘` + `0` on macOS or `Ctrl` + `0` on Windows to return to 100%.
### Fit Selection
Press `F` to fit the current selection in the viewable stage area.
If an object is selected, Rive fits that object in view. If an artboard is selected, Rive fits the artboard in view.
If you lose track of an artboard or object on the stage, select it in the hierarchy and press `F` to fit it in view.
# Sidebar
Source: https://rive.app/docs/editor/interface-overview/sidebar
Use the sidebar to access editor panels and organize your workspace.
The sidebar is the left side of the editor. It contains panels for navigating your file, managing assets, working with data, editing animations, and using the AI Agent.
## Panels
The sidebar includes several panels:
* [Hierarchy](/docs/editor/interface-overview/hierarchy) - View and organize the objects in your file.
* [Data](/docs/editor/data-binding/overview) - Create and manage View Models, properties, and data binding.
* [Assets](/docs/editor/fundamentals/assets-overview) - Manage images, fonts, audio, and other assets used in your file.
* [Animations](/docs/editor/animate-mode/animate-mode-overview) - Create and manage animations and State Machines.
* [Agent](/docs/editor/ai-agent/ai-agent) - Use the AI Agent to help create, edit, and explore your file.
## Panel Options
Use the **…** menu on a panel to access options such as filtering, sorting, collapsing the panel, or resetting the sidebar layout.
## Organizing Panels
You can customize how panels appear in the sidebar.
* Toggle full sidebar height.
* Drag panels to change their order.
* Drag a panel into its own column.
### Open AI Agent in Viewport
The Agent panel also has an option to open in the viewport, where it appears alongside the Stage or Script panel.
# Viewport
Source: https://rive.app/docs/editor/interface-overview/stage
Use the viewport to work with the Stage, scripts, and the AI Agent.
The viewport is the center area of the editor. It can show the [Stage](#stage), scripts, and the **AI Agent**.
You can [split the viewport](#splitting-the-viewport) to view multiple panels side by side or stacked vertically. This is useful when you want to edit artwork while viewing a script, or keep the AI Agent open alongside the Stage.
## Stage
The Stage is an infinite canvas where you can place artboards containing all your graphics.
[Artboards](/docs/editor/fundamentals/artboards) live on the stage. You can pan and zoom around the stage, select objects, and use guides or rulers to help position elements.
To learn how to navigate and select items on the stage, see [Keyboard Shortcuts](/docs/editor/keyboard-shortcuts).
### View Options menu
The View Options menu controls what appears on the stage while you work. Use it to adjust zoom, snapping, guides, and visual overlays for animation and interaction tools.
* **Zoom and snapping:** adjust the stage zoom level, enable snapping, or snap objects to pixels
* **User Cursors:** show or hide other users' cursors
* **Tool overlays:** show or hide gizmos, bones, targets, motion paths, joysticks, events, layouts, and text modifier ranges
* **Show Final Playback:** show the final playback state while editing
* **Rulers and guides:** show rulers, lock guides, or clear guides from the stage
### Stage Background Colors
You can set separate stage background colors for Design mode and Animate mode.
Using different colors can make it easier to tell which mode you are in at a glance. This can help prevent accidentally editing keys or animation values when you meant to work in Design mode.
To change the stage background colors, deselect everything, then use the background color options in the [Inspector](/docs/editor/interface-overview/inspector).
Stage background colors are only visible in the editor. They do not affect the artboard background, exports, or runtime appearance.
### Rulers and Guides
## Splitting the Viewport
To split the viewport, drag a panel into the viewport or click **Split Editor Right** from the panel options.
To open the **AI Agent** in the viewport, click the **…** button in the **Agent** panel in the sidebar, then select **Open in Viewport**.
# Toolbar
Source: https://rive.app/docs/editor/interface-overview/toolbar
Access file, design, rigging, preview, and export tools from the Rive editor toolbar.
Use the toolbar to switch tools, create objects, adjust view options, invite collaborators, publish your file, and access file-level actions.
## Editor Menu
The **Editor menu** contains file-level actions and editor settings.
Common actions include:
* [Revision History](/docs/editor/fundamentals/revision-history)
* [Export for Runtime](/docs/editor/exporting/exporting-for-runtime)
* [Export for Backup](/docs/editor/exporting/exporting-for-backup)
* [Share and Embed](/docs/editor/embed-urls/overview)
* [Render to Video or Image](/docs/editor/exporting/exporting-for-video-and-static-design)
## Tools
The **Tools** area lets you select, transform, draw, create objects, add rigging, and preview interactions.
Common tools include:
* Select, translate, rotate, and scale tools
* [Artboards](/docs/editor/fundamentals/artboards) and [components](/docs/editor/fundamentals/components)
* [Shape and drawing tools](/docs/editor/fundamentals/shapes-and-paths-overview)
* [Bones tools](/docs/editor/manipulating-shapes/manipulating-shapes)
* Play the [State Machine](/docs/editor/state-machine/state-machine)
## Invite
Use **Invite** to add collaborators to the file.
## Publish
Use **Publish** to export, embed, or publish your file.
* [Export a .riv for runtime](/docs/editor/exporting/exporting-for-runtime)
* Publish to a [library](/docs/editor/libraries)
* [Embed](/docs/editor/embed-urls/overview)
* Publish to the [marketplace](/docs/community/marketplace-overview#marketplace-overview)
## Preview Bound Values
By default, data-bound values update while the state machine is playing.
Turn on the **Data Binding Preview Toggle** to preview bound values while editing.
## Mode Toggle
Use the **Mode Toggle** to switch between **Design** and **Animate** mode. Press `Tab` to switch modes quickly.
* **Design Mode**: create, edit, import, and rig graphics.
* **Animate Mode**: create timelines, state machines, keys, and animation logic.
Editing objects while a timeline is selected in Animate Mode can create keys. Learn more about [Design and Animate Modes](/docs/editor/fundamentals/design-vs-animate-mode).
\\
# Keyboard Shortcuts
Source: https://rive.app/docs/editor/keyboard-shortcuts
Find and use keyboard shortcuts for tools, navigation, editing, and playback in the Rive editor.
## Tools
| Tools | Shortcut |
| ----------------------------------------------------------- | ------------ |
| Select Tool | V |
| Translate Tool | Q |
| Rotate Tool | W |
| Scale Tool | E |
| Rectangle Tool | R |
| Ellipse Tool | O |
| [Pen Tool](/docs/editor/fundamentals/pen-tool-overview) | P |
| [Artboards](/docs/editor/fundamentals/artboards) | A |
| [Bones](/docs/editor/manipulating-shapes/bones) | B |
| [Groups](/docs/editor/fundamentals/groups) | G |
| [Freeze and Origin](/docs/editor/fundamentals/freeze-and-origin) | Y |
## Editor
| Purpose | Shortcut |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| Switch Mode | Tab |
| Constrain Shape/Angle | Shift |
| Deep Select | macOS: ⌘ + click Windows: Ctrl + click |
| Select Behind | Alt |
| Nudge Object | Arrow Keys |
| Edit Vertices | Enter w/ path selected |
| Group Selection | macOS: ⌘ + G Windows: Ctrl + G |
| Ungroup Selection | macOS: ⌘ + Shift + G Windows: Ctrl + Shift + G |
| Convert to Solo | macOS: ⌘ + L Windows: Ctrl + L |
| Duplicate | macOS: ⌘ + D Windows: Ctrl + D |
| Layer Forward | macOS: ⌘ + ] Windows: Ctrl + ] |
| Layer Backward | macOS: ⌘ + \[ Windows: Ctrl + \[ |
| Layer to Top | macOS: ⌘ + Opt + ] Windows: Ctrl + Alt + ] |
| Layer to Bottom | macOS: ⌘ + Opt + \[ Windows: Ctrl + Alt + \[ |
| Move Timeline Playhead | , or . Hold Shift to move 10 frames |
| Move | |
| Move Selected Keys | Alt + , or . Hold Shift to move 10 frames |
| Skip to Keys (Selected Row, Otherwise All) | macOS: ⌘ + , or . Windows: Ctrl + , or . |
| Toggle Snapping | macOS: hold ⌘ Windows: hold Ctrl |
| Color Picker | macOS: Ctrl + C or I Windows: I |
| Search | macOS: ⌘ + K Windows: Ctrl + K |
| Reveal Keys for Selection | U |
| Play Default State Machine | Shift + Space |
## View
| Purpose | Shortcut |
| ----------------------- | ------------------------------------------------------------------------ |
| Fit selection to screen | F |
| Zoom (mouse) | macOS: ⌘ + Mouse Wheel Windows: Ctrl + Mouse Wheel |
| Zoom (keyboard) | + and - |
| Zoom (marquee) | Z (hold) |
| Pan | Right-click + drag Space + drag |
## Hierarchy
| Purpose | Shortcut |
| ---------------------------- | ------------------------------------------------------------------------------------- |
| Select parent | Esc |
| Select 1st child | Enter |
| Rename | macOS: ⌘ + R Windows: Ctrl + R |
| Expand/Collapse all children | macOS: Opt + click expand icon Windows: Alt + click expand icon |
| Show/hide self only | macOS: ⌘ + click eye icon Windows: Ctrl + click eye icon |
| Show/hide parents only | macOS: Opt + click eye icon Windows: Alt + click eye icon |
## See Shortcuts in the Editor
**Show Shortcuts** lets you view and search all keyboard shortcuts in the Rive editor.
To open it, go to the **Editor** menu and select **Show Shortcuts**.
# Component Sizing
Source: https://rive.app/docs/editor/layouts/component-sizing
Control how component instances scale, fit, and reflow within their parent.
Component sizing determines how a component instance responds to the space available from its parent. Choose a sizing mode based on how you want the component and its contents to respond as that space changes.
* **Node** - Scale the component as a single object.
* **Leaf** - Fit the component's contents within the available space.
* **Layout** - Resize the component's artboard, allowing its contents to reflow.
## Node
**Node** is the default sizing mode. The component behaves like a regular object or group, with its contents scaling together based on the **Scale** property.
Use Node when the component doesn't need to respond to the size of a parent Layout.
## Leaf
**Leaf** fits a component's contents within the space provided by its parent Layout or artboard. The component's contents scale rather than reflow.
Leaf is useful for responsive components that don't contain Layouts themselves, such as an illustration or icon that needs to fit within the available space.
### Fit
**Fit** determines how the component's contents scale within the available space.
* **Fill (Default)** - Fill the available space. If the aspect ratios differ, the content stretches to fit.
* **Contain** - Fit all content within the available space while preserving its aspect ratio. This may leave unused space.
* **Cover** - Fill the available space while preserving the content's aspect ratio. Some content may extend beyond the available space.
* **Fit Width** - Scale the content to the width of the available space. This may result in clipping or unused space vertically.
* **Fit Height** - Scale the content to the height of the available space. This may result in clipping or unused space horizontally.
* **None** - Keep the content at the original size of its artboard. This may result in clipping or unused space.
* **Scale Down** - Scale the content down to fit while preserving its aspect ratio, but don't scale it up when the available space is larger.
### Alignment
**Alignment** determines where the component's contents are positioned when they don't fill the available space. Choose from nine positions using the alignment grid, including center, corners, and edges.
For more precise positioning, use **Alignment Position X/Y**. Values range from `-1` to `1` on each axis:
* **X:** `-1` is left, `0` is center, and `1` is right.
* **Y:** `-1` is top, `0` is center, and `1` is bottom.
You can also use values between these positions. For example, an X value of `0.5` positions the content halfway between center and right.
## Layout
**Layout** resizes the component's artboard instead of scaling its contents. As the artboard changes size, Layouts within the component can reflow their contents to respond to the available space.
Use Layout for responsive components whose internal content needs to resize or reflow, such as buttons that hug their text or components that fill the width of a parent Layout.
### Size
In Layout mode, the component's width and height can each use one of three sizing behaviors:
* **Fixed** - Use a defined width or height. Fixed dimensions can use either units or percentage values.
* **Hug** - Resize to fit the component's contents. This is useful when content such as text needs to determine the component's size.
* **Fill** - Expand to fill the available space within the parent Layout or artboard.
When using **Fixed**, enter a value for the component's width or height and use the unit control to switch between units and percentages.
Unlike the **Scale** property, changing the size of a component in Layout mode changes the dimensions of its artboard and allows its contents to reflow. In most cases, avoid using **Scale** with components in Layout mode.
# Layout Animation
Source: https://rive.app/docs/editor/layouts/layout-animation
Animate how children reposition when content reflows within a Layout.
When a Layout resizes or its content changes, its children may move to new positions. Layout animation controls how children transition to their new positions, including the duration and easing.
## Adding Layout Animation
Select a **Layout** and click the **+** button in the **Enable Layout Animation** section of the Inspector.
* **None:** Disable Layout animation.
* **Inherit:** Use the Layout animation settings from the parent Layout.
* **Custom:** Define Layout animation settings for the selected Layout.
If you select **Custom**, set the duration and easing for the animation.
# Parameters
Source: https://rive.app/docs/editor/layouts/layout-parameters
Layout parameters can broadly be grouped into one of two categories — those that affect the parent layout, and those that affect the child layouts.
In Rive, it's important to remember that layout parameters generally only affect other layout containers. This can often result in the nesting of layouts for desired results, but unlocks opportunities to blend with Rive's more freeform canvas in creative ways.
***
## Absolute vs Relative
To control the flow of child Layouts in a Row or Column requires the Layout children to be Relative, i.e. not have their Absolute position option enabled. Use the icon in the top right of the Layout inspector to toggle between an Absolute and Relative Layout.
* **Absolute:** An Absolute Layout positions itself within an artboard or parent Layout container. It can have 2 or more of it’s edges pinned to the container.
* **Relative:** A Relative Layout has it’s position defined by the parent artboard or Layout. Changing the flex properties on the parent will determine the child layout behaviour.
## Scale Types
A layout’s width and height can make use of 3 different scale behaviours:
* **Fixed:** A fixed width or height for the Layout. The defined value can be either a point or percentage value. Use the unit toggle within the fields to toggle between value types.
* **Hug:** The width and/or height of the Layout shrinks to fit its children. For example, you may want a Layout to hug a text object; resizing itself based on the length of the text inside.
* **Fill:** The width and/or height of the Layout expands to fill the available space within the parent Layout or artboard. The fill option switches the width/height fields to be represented in `fr` (fill ratio) units and reveals a base size field. The fill ratio values make it possible to control the fill behaviour between a number of children with the fill scale behaviour. For example, you may want the children to fill the available space equally, or perhaps have one child scale at a greater factor than the rest.
Options to set the scale type are below the width and height fields in the inspector.
Hug and Fill options will only surface when applicable. For example, a Layout without any children won’t surface the option to hug, while only a Layout with another Layout as its parent can be set to Fill.
## Size constraints
Use the icon above the absolute toggle to add minimum and/or maximum width and height values. As with the width and height values themselves, these can be defined as either points or percentages.
## Clip
The clip toggle hides any child elements within the layout that extend beyond the bounds of the Layout.
## Position (Absolute Layouts only)
Absolute Layouts provide additional options to set their position within the artboard or parent Layout. The position is defined by at least 2 pinned edges — one horizontal and one vertical, however additional edges can also be enabled.
Set the desired edges to pin by selecting the markers within the inspector graphic, or via the fields below. You can hold `shift` while selecting a marker to add a second edge along the same axis. The distance values from a chosen edge can be provided as points or percentages by selecting the chosen unit type within the field.
## Padding and Margin
Padding ONLY affects Layout children. For example if you want to have a button with a label, the text should be wrapped in a Layout and that Layout should be the child of another Layout that applies the padding.Margin affects the Layout to which the margin is applied relative to its parent Layout.
Padding and margin can be applied symmetrically along an axis, or to individual edges. Use the edge toggle to reveal fields for each edge plus options to use point or percent values.
* **Padding:** Inner space between the Layout bounds and any relative Layout children.
* **Margin:** Outer space between the Layout bounds and a relative parent Layout.
## Layout Children
A layout’s flex parameters are only applied to other layouts contained within it. In order to generate rows, columns, and grids of content that reflow, content items must themselves be wrapped in an additional layout container.
## Row/Column
* **Row:** Lay out children along the horizontal axis.
* **Column:** Layout children along the vertical axis.
* **Row Reverse:** Lay out children horizontally, in reverse order.
* **Column Reverse:** Lay out children vertically, in reverse order.
## Wrap
* **No Wrap:** When the content reaches the bounds of the layout, continue to extend beyond it.
* **Wrap:** When the content reached the bounds of the layout, place the next item on below or alongside the currently row/column.
* **Wrap Reverse:** Same as wrap, but display the content in reverse order.
## Alignment
Select the desired point on the inspector widget to align content within a layout container.
## Justify
Click on the current alignment position to expand the content to fill the available space. Selecting the one of the active tiles again collapses the content back down.

## Gap
Spacing between content (gaps) can be set both horizontally and vertically, and as either points or percentages of the container width/height.
## Left-to-Right/Right-to-Left
Determines the horizontal direction for this Layout and will also be cascaded down to its child Layouts (when its child Layouts are set to Inherit). This will change the direction of Row based Layouts as well as Text alignment within any Layouts. This is useful when there is a requirement to support Right-to-Left languages.
## Styles
Layouts can have fills, strokes, feathers, blend modes, and corner radii, just like other objects in Rive.
Styles can be rendered in the **background**, behind the Layout's children, or in the **foreground**, in front of them. For example, you might place a fill and feathered shadow behind the children while rendering a stroke in front.
Corner radius applies to the Layout's styles as well as its clipping bounds.
# Tools
Source: https://rive.app/docs/editor/layouts/layout-tools
There are several Layout tools available in Rive to build your responsive UI or content
## Getting Started with Layouts
There are a number of ways to start adding layouts to your designs.
* Layout Tools in the Arrangement Tools menu
* Wrap in Layout
* Add Child Layout
* Dragging and Dropping Layouts
***
### Layout Tools in the Arrangement Tools Menu
* **Layout:** A single Layout container. Select the tool and drag on an artboard to create the Layout. Drag directly onto the Artboard to create an Absolute Layout or on top of an existing Layout to create a Relative child. In addition, any objects positioned within the bounds of the newly created Layout will automatically themselves be wrapped in Layouts and absolutely positioned inside the new Layout.
* **Row/Column:** The Row and Column tools create a Layout in the same way as the Layout tool above, but also include an initial set of children which will be positioned in either a Row or Column. You can use either the number keys or the up/down keys to define the number of children while dragging your row or column onto the stage. The created children will have their widths and heights set to **Fill**.
Absolute Layouts can be dragged around or resized like other Rive objects. By default, if you drag a Layout (or any other object) inside the bounds of another Layout, it will display an indicator showing you that the dragged object will become a child of that Layout if dropped. Hold `command` / `control` while dragging the object to prevent existing items from being moved into the Layout.
***
### Wrap in Layout
Instead of starting with an empty Layout container, you can wrap existing objects into a Layout. There are a number of ways to wrap an active selection in a Layout:
* Right click on the stage or the hierarchy to surface the context menu. Select `Wrap in` > `Layout`. You can do this for a single or multiple objects at once.
* Use the `shift` + `L` shortcut.
* Alternatively, you can use the `Layout selection` button in the inspector. This is available when only non-Layout objects are selected.
***
### Add Child Layout
When a Layout is the current selection, an `Add Child Layout` button will appear in the Layout inspector. Clicking this will add a new Layout as a child of the selected Layout, with its width and height set to Fill.
***
### Dragging and Dropping Layouts
Layouts (both Absolute and Relative) can be dragged and dropped into other Layouts at any time. This can be done in two ways:
* Drag and drop Layouts in the hierarchy panel.
* Drag and drop Layouts directly on the stage. When doing so, an indicator will show where in the new parent Layout the dragging Layout will be inserted.
Layouts can also be deleted by selecting the Layout on stage or in the hierarchy and hitting the delete key.
# Overview
Source: https://rive.app/docs/editor/layouts/layouts-overview
Layouts allow you to build responsive UI components in Rive. Make your designs fit, fill, or reflow content based on the space available.
Leverage Rive's layout system to accommodate a variety of use cases:
* Pin items to chosen edges of a parent artboard or container.
* Create buttons and labels that adapt to the size of the text.
* Build lists and grids of content that reflow, animate, and scroll.
* Combine and nest layouts to develop entire interfaces
You can use these techniques to create all kinds of production-ready buttons, lists, and menus that can fluidly resize to fit any device size or orientation. Rive graphics aren’t mockups or prototypes, they’re functional graphics that can change state and be connected to real data — and because Rive runs anywhere, you can re-use the same responsive graphics on mobile apps, game engines, websites, custom devices, and more.
Check out the [Layouts playlist](https://www.youtube.com/playlist?list=PLujDTZWVDSsGvor80PkjHaZ3hNNo6s_ef) on Rive's YouTube channel for additional Layout related tutorials
***
## Introduction
Prior to the addition of Layouts in Rive, all objects on an Artboard were positioned in a freeform way, with few rules limiting this (one exception being [Constraints](/docs/editor/constraints/constraints-overview)). Layouts provide a rules based way to position and size your content using Rows and Columns.
A Layout is a container whose position and size is bound by rules (relative to its parent Layout or children). When Rive objects (text, shapes, paths, groups, images, components, and even joysticks or bones) are placed in a Layout, they inherit the positioning rules of the Layout (**participate in the Layout**), but they can also act independently within the Layout container if desired. This allows for added freedom if you need to animate an object within a Layout.Layouts only affect the position of other Layouts. For example, if you want to have a Row Layout, you would have a parent Layout set to a direction of Row, and all of its Layout children will be laid out in a row (and have things like their parent's padding, gap, alignment, etc. applied to their positions).
***
## Layout Parents and Children
In order to create more complex UI that responds to the screen or browser size, it is important to understand that Layouts can be placed inside other Layouts. We refer to the outer Layout as the parent and the inner Layout as the child. The Layout children are typically positioned relative to their Layout parent (similar to how [Groups](/docs/editor/fundamentals/groups) work). In addition, the parent can either be sized to the children ([Hug](editor/layouts/layout-parameters#scale-types)) or the children can size to their parent ([Fill](editor/layouts/layout-parameters#scale-types)). Here is an example to help visualize how these relationships work.
In the image below we will focus on the regions contained by the dashed lines. The outer green dashed line is the outermost Layout, which is set to Layout its children in a single Column. In the second row of that Column, the red dashed line is a child Layout containing the Battery indicators for various devices. This Layout is defined as a Row. This Layout has 4 child Layouts (blue dashed) set to evenly Fill the width of their parent (so that when the parent resizes, the child Layouts also resize, each to Fill 25% of the available space). Each of those 4 Layouts are set to Column and have 2 child Layouts (pink dashed) containing a trim path with a visualization of battery remaining and percentage label. By creating these simple parent-child Layouts, we can create infinitely responsive content with Rive!
***
## Absolute vs Relative Layouts
Layouts can exist within Rive's freeform transform space, which means that you can draw a Layout to the Artboard and position it as you would any other Rive object. This type of Layout is referred to as **Absolute** (positioned absolutely).
On the other hand, when you want a Layout to participate in the flow of its parent layout, this is referred to as **Relative** (positioned relative to its parent). The position of Relative Layouts are determined by their parent via many parameters such as Row/Column, alignment, padding, gap, etc.
Use the icon in the top right of the layout inspector to toggle between an absolute and relative layout.
***
## Layouts and other Rive objects
A Layout container will affect its children in one of two ways:
* Set the child’s position
* Set both the child’s position and size
This behaviour is determined by the object type of the child. Objects that have both their position and size defined by a Layout container include:
* Text
* Images
* Parametric shapes (rectangles, ellipses, triangles, polygons, and stars)
* Component instances (Leaf & Layout mode)
* Other Layouts
All other objects will only have their positioned set by the layout. The [N-Slicing](/docs/editor/layouts/n-slicing) feature provides more advanced options to control the layout/scale behaviour of more advanced shapes and groups.
Unlike in some other tools, Rive will provide an additional hierarchy item to represent the Layout container of an object. This helps differentiate the freeform nature of Rive with the structured Layout system. For example, an object within a Layout container can still apply additional transforms such as position, scale, and rotation to allow it to break out of the Layout. This becomes particularly powerful when coupled with constraints. Furthermore, a Layout container can house multiple objects that can be placed in front of each other.
***
## Use cases
#### Building a Responsive Button
This tutorial shows how you can build a responsive button from scratch.
#### Reflow With Dynamic Components
This tutorial explains how to build Rive files that can reposition their elements dynamically when resized.
If layouts aren't visible on the stage, open the **View Options** menu and make sure **Layouts** is enabled.
# N-Slicing
Source: https://rive.app/docs/editor/layouts/n-slicing
## What is N-Slicing?
Rive's N-Slice feature is inspired by the 9-slicing technique commonly used in game design. A 9-slice is typically applied to an image to prevent the four corner segments from scaling when resized. Meanwhile, the remaining five inner segments stretch or tile to allow raster artwork to scale up and change ratio without distorting.
N-Slicing takes things a step further; allowing you to create any number of segments, to both raster and vector artwork within Rive.
## Creating an N-Slice
There are two types of N-Slice — those applied to images and those applied to vector objects in the form of a group. Whilst much of the functionality between them is shared, there are some subtle differences, starting with the way you apply them.
**Image/Raster N-Slice**
Select the image you want to apply an N-Slice to.
Select the add action within the Deform section of the inspector and choose the N-Slice option. Alternatively, right-click the image on the stage or in the hierarchy and navigate to the N-Slice option within the Deform submenu."
Upon creation, the N-Slice will enter edit mode. Here you can set up the axes and tile modes that define the scale behaviour of the image. Start by positioning and/or creating the axes as desired. Once completed, select the 'Done' action in the inspector or stage prompt. Learn more about how to best setup your N-Slice in the 'Setting up an N-Slice' section below.
To return to the edit mode, select the 'Edit N-Slice' action in the inspector or right-click menu.
**Group/Vector N-Slice**
If your selection is already contained within a group, select the group on the stage or in the hierarchy and use the 'Convert to N-Slice' action in the inspector. Alternatively, select the stage objects you'd like to wrap in an N-Slice group and choose the 'N-Slice selection' action in the inspector. You can also reach this option by right-clicking on the items within the stage or the hierarchy and navigating to the 'Wrap in' submenu.
With the N-Slice group selected, use the 'Edit N-Slice' option in the inspector (or press `enter`) to enable the edit mode. From here, you can setup your axes and tile modes. Once completed, select the 'Done' action in the inspector or stage prompt. Learn more about how to best setup your N-Slice in the 'Setting up an N-Slice' section below.
## Setting up an N-Slice
With your image or N-Slice item selected, use the inspector action, right-click menu, or `enter` key to enter the edit mode. The edit mode allows you to change the configuration of your axes and tile modes to define how your image or group scales.
We recommend setting up your N-Slice *before* scaling the image or changing the size of the group. While it's possible to adjust the axes on an already-scaled image, accurately positioning new ones can be more difficult.
The scale behaviour of segments within an N-Slice alternate, starting with a fixed segment. With a regular 9-slice, this results in a fixed segment, followed by a scaling one, and ending with another fixed. This behaviour is applied along both axes. You can identify a fixed segment from a scaling one by the solid blue borders displayed in edit mode. Meanwhile, scaling segments are identifiable via dashed borders.
**Creating and positioning axes**
By default, a new N-Slice is created with 4 axes — 2 vertical and 2 horizontal. Together they divide the content into 9 segments. Click and drag the existing axes to reposition them, or adjust their values in the inspector. Values can be defined as points or percentages.
To create new axes, click and drag from the outer bounds of the image or group. You can identify the bounds via the white handles positioned on each edge. By default, a new axis will be created with a mirrored counterpart. This helps maintain the alternating fixed/scale behaviour. However if you'd prefer to create a single axis, hold `command` / `control` before dragging the new axis.
Hold `command` / `control` while dragging a new axis to prevent a mirrored counterpart being created at the same time.
**Setting tile modes**
Tile modes can only be changed on image/raster based n-slices.
Tile modes determine the behaviour of a **scaling segment**. There're 3 tile mode options to choose from:
* **Stretch:** Stretches the segment as the image is resized.
* **Repeat:** Tiles the segment repeatedly as the image is resized.
* **Hidden:** Hides the segment, not rendering it at all.
Tile modes other than 'hidden' won't have any effect on a fixed segment.
To change the tile mode of a segment:
With the image or its N-Slice selected, use the 'Edit N-Slice' option in the inspector (or press `enter`) to enable the edit mode.
Identify the segment you want to change by selecting it on the stage. The corresponding tile will be highlighted in the inspector.
Set the tile mode via the dropdown menu in the inspector.
# Libraries
Source: https://rive.app/docs/editor/libraries
Publish your components with dynamic data once, and reuse them everywhere in your project.
Libraries are available on Voyager and Enterprise plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
## Introduction
Libraries facilitate the sharing of [components](editor/fundamentals/components) and their view models across Rive files. In the past, you may have relied on nested artboards and copy-paste workflows to share elements. This works for individuals, but breaks down at scale: exports bloat, versions drift, and teams lose track of changes.
With Libraries:
* Components can be published and reused across project files.
* Updates flow downstream with version history and change notifications.
* Teams can collaborate without worrying about mismatched assets.
Libraries are available on Voyager and Enterprise plans. [Learn more about our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
## Creating a Library
Any file can be made into a library. First, you'll need to create a [component](editor/fundamentals/components) and/or a view model before you can publish the file as a library. Select an artboard on the stage and use the component icon in the inspector or `Shift` + `N` to toggle its status as a component.
With a component or view model present in the file, select the **Publish Library** option via the export action on the toolbar, or via the file menu. The publishing panel provides an opportunity to select which of the components, view models, and enums you'd like to be published as part of the library.
You can identify a library file by the icon in the tab bar and in the file browser.
## Importing from a Library
To start using components and view models from a published library, select the library icon present in both the asset and data panels. The library panel displays alongside the hierarchy/asset/data column, and displays a list of the available libraries for the active file.
Currently, a Rive file can only access libraries contained within the same project. Cross-project — or workspace — libraries are coming soon. Public libraries are planned soon after that.
Selecting a library listed in the panel will present its available components, view models, and enums. Choose the elements you'd like to add to your file, and use the **Add to File** action in the inspector to import them.
You can use the version dropdown to browse and import previous iterations of libraries and their components.
With the chosen elements added to your file, you can access and reuse components and view models via the asset and data panels. Elements sourced from a library can be identified by the library icon appended to the bottom-right corner of the regular icon.
## Updating a Library
After publishing, you can continue to make changes, add, or remove components, view models, and enums. Republish the library via the same option in the export or file menus. Upon publishing an updated version of the library, any files that have imported elements from it will display a small badge indicating an available update.
To update a component, right-click it in asset panel and select **Library Options** -> **Update Component** from the context menu. Choose the library elements you'd like to update and select **Update Selected**.
## Detaching
After importing a component from a library and creating an instance of it on the stage, you may choose to detach it. Detaching a component will decouple it from the source and copy over its contents into your active file. Any references to it will be redirected to the new local copy. You may want to detach a component to make changes to it, without changing the source.
It's not possible to re-attach a component after it has been detached.
## Export Options
By using libraries, you create a series of dependencies between files. For example, an instance of an imported component depends on the library file it came from, which in turn may depend on an image asset used within the component, or perhaps another component from a different library entirely.
The export options control what and how components and any assets they depend on get exported to a riv file. These options are available across assets and components, with an additional layer of separation for libraries specifically.
To access the export options for a given component, asset, or library, select it in the asset panel and set the desired options in the inspector.
* **Automatic:** Include this asset/component in the export if it's being used somewhere on the stage. For assets contained within a library, this option is inherited from the source file.
* **Force Export:** Export this asset/component, regardless of whether or not it's referenced within the file.
* **Prevent Export:** Don't include this asset/component in the export, regardless of whether or not it's referenced within the file.
You may want to adjust these options to suit your needs at runtime as opposed to design-time. For example, images used within a design are to be supplied from an external source at runtime, and therefore aren't required in the riv file. Or, you intend to use a library component across multiple Rive files at once via data binding. Exporting the library component separately and excluding from the host files prevents it from being duplicated.
Currently, the export behavior for assets within a library component can only be set at the library level, not per component. To do so, set the panel display mode to Source/Type, and select the library item in the asset panel.
## Unpublishing a Library
Use the **Unpublish Library** action in the file menu to prevent additional files from accessing the library and its components. Unpublishing does not remove library components from host files that had already imported them. Host files that have imported library components will retain access to previously published versions of the library. You may republish a library at any time.
# Bones
Source: https://rive.app/docs/editor/manipulating-shapes/bones
Bones create a skeleton you can use to pose and animate elements. Connect bones in a chain, then move or rotate them to control anything from a character’s arm to a bending branch or waving flag.
Bones can control elements in three ways:
* **Transform an element:** Make a shape, image, group, or other element a child of a bone. The entire element follows the bone’s position, rotation, and scale.
* **Deform a vector:** Bind the vertices and Bézier handles of a vector path to one or more bones. Adjust their weights to control how the path bends.
* **Deform an image:** Add a mesh to a raster image, then bind its vertices to one or more bones. Weighted vertices allow the image to bend and stretch smoothly.
You can combine these methods in the same rig. For example, a character’s hand might be parented to an arm bone while the sleeve’s vertices are weighted across several bones to create a natural bend.
## Creating Bone Chains
Select the Bone tool from the **Bones** menu, or press **B**.
Click once to place the start of the first bone, then click again to set its length and rotation. Continue clicking to add bones to the chain. Each new bone becomes a child of the previous bone.
Press Esc or switch to the **Select** tool (**V**) when you’re finished.

To extend an existing chain from a different point, select the [joint](#joints) where you want the new branch to begin. Then select the Bone tool and continue clicking to add bones.

If you can’t see your bones on the Stage, open **View Options** and make sure **Bones** is enabled.
### Joints
Joints are the points where bones connect. Use them to position and orient bones while building your chain.
Joints do not appear as separate objects in the Hierarchy. Moving a joint changes properties such as the length and rotation of the bones connected to it.
### Root bone
The first bone in a chain is the **root bone**. It controls the position of the entire chain and is the only bone in the chain with X and Y position properties. Every other bone is positioned by its length and rotation relative to its parent.
## Controlling Elements With Bones
### Transforming Elements
Make an element a child of a bone to control its transform. When the bone moves, rotates, or scales, its children follow without being deformed.
To connect an element, drag its layer onto the bone in the Hierarchy. You can parent shapes, images, groups, and other bone groups to a bone.
This method works well for rigid parts that should keep their shape, such as a hand, shoe, or mechanical component.
### Deforming Elements
You can bind bones to the control points of both vector paths and raster images:
* Vector paths use vertices and Bézier handles.
* Raster images use the vertices of a [mesh](/docs/editor/manipulating-shapes/meshes).
Binding allows different bones to influence different parts of an element, creating smooth bends and other deformations.
Before binding a procedural shape, such as a rectangle or ellipse, convert it to a custom path. Select its path layer, then press `Enter`.
Select the element you want to deform, such as a vector shape or a raster image with a [mesh](/docs/editor/manipulating-shapes/meshes).
In the **Bind Bones** section of the Inspector, click the **+** button.
Hold **Cmd + Shift** on macOS or **Ctrl + Shift** on Windows, then select the bones you want to bind.
Click **Done**.
After binding, Rive assigns each bone a color. The same colors appear on the bone and in the Bind Bones section, helping you identify which bones influence each control point.
Rive automatically assigns an initial influence, or weight, to each vertex. Vector paths also allow you to weight their Bézier handles. Adjust these weights to fine-tune how the element deforms.
#### Adjusting Vertex Weights
A weight determines how much influence a bone has over a vertex. A vertex can be influenced by one or more bound bones, but its combined weights always equal 100%.
After binding, each vertex displays a pie chart using the colors assigned to its bones. The size of each colored segment represents how much that bone influences the vertex.
Rive assigns initial weights automatically. To adjust them, select a vertex or group of vertices, then change the weight for each bone in the Bind Bones section of the Inspector.
Vertices near a joint often benefit from being influenced by both neighboring bones. Blending their weights creates a smoother bend as the bones move.
On vector paths, you can weight a vertex and its Bézier handles independently for more precise control over the deformation.
[Try it in the Rive Editor](https://rive.app/community/files/28349-53555-control-bezier-handles-with-bones?utm_source=docs\&utm_medium=docs_link_card\&utm_campaign=docs_to_marketplace_links).
You can lock a vertex’s weight for a specific bone. Locked weights stay fixed when you adjust other bone weights on the same vertex.
#### Weight Editing Tools
Rive includes tools to help you adjust vertex weights after bones are bound.
**Edit Weights** lets you adjust the influence of a specific bone on selected vertices. Select a vertex, then select a bone on the Stage or in the **Bind Bones** section. Drag up or down on the vertex to increase or decrease that bone’s weight.
**Smooth** evens out bone weights across neighboring vertices. If vertices are selected, smoothing applies to the selected vertices. If no vertices are selected, it applies to the entire shape.
**Auto-Weights** automatically assigns weights based on each vertex’s nearest bone, blending where neighboring bone regions meet. If vertices are selected, auto-weights apply to the selected vertices. If no vertices are selected, they apply to the entire shape.
Auto-Weights includes two settings:
* **Blend** - controls how widely weights blend at the boundary between two bones. Higher values spread the blend across more of the mesh, creating softer deformation. Lower values create a more rigid result, where each vertex sticks more closely to its nearest bone.
* **Influence** - controls the maximum number of bones that can influence each vertex.
#### Mesh View Options
**Show Weights** shows or hides weight pie charts on unselected vertices. Selected vertices always show their weights while you edit them.
For other mesh view options, such as isolating the mesh or showing and hiding triangles, see [Meshes](/docs/editor/manipulating-shapes/meshes#mesh-view-options).
# Clipping
Source: https://rive.app/docs/editor/manipulating-shapes/clipping
Clipping allows you to cut one shape out from another.
## How to use Clipping
Select the shape or group you want to clip and hit the plus button next to the Clipping options in the Inspector. After hitting the plus button, you'll notice a blue border appear around the stage, indicating that you can pick a shape as the clipping source. Now, select the path you want to use as a clipping path. Remember, the clipping target must be a shape, not a group or any other object.
You can add as many clipping paths to a shape as you'd like.
## Clipping and path direction
If you have shapes that aren't clipping, or only partially clipping, be sure to check the winding of that shape. In most cases, reversing the direction of the path fixes this problem.
## Inverse Clipping
Clipping is typically used to hide a part of your graphics. In the example below, we're using an ellipse to show only part of our jewel graphic.
You occasionally may want to invert the clipping, so that only the graphics outside of the clipping paths are drawn.
This is achieved using a clipping path that looks like the gray shape in the image below.
To create this shape, draw a rectangle the size of the artboard. Add both the rectangle path and ellipse path to the same shape layer in your Hierarchy.
Note that your shape might not show a hole as ours does. That's because you need to set the Fill Rule of your shape to Even-Odd. This setting doesn't affect your Clipping Path, but it helps explain how the Even-Odd operation works, which will be useful later!
Select the group containing the jewel and use the "plus" icon in the Clipping section of the Inspector. Next, select the Clipping Shape as the target.
Open the Clip Options and set the Operation to Even-Odd.
Be sure to hide the visibility of your clipping shape so it doesn't cover your graphic.
# Joysticks
Source: https://rive.app/docs/editor/manipulating-shapes/joysticks
Joysticks make it easy to set up sophisticated rigs with simple controls. You can quickly animate body poses, eyes, mouths, hands, and more. You can even control joysticks with other joysticks.
Joysticks work by allowing you to pan through different timelines that are assigned to either the X or Y axis of the joystick. Once you’ve assigned the timelines, you can use the joystick to set keys to create animations.
Either watch the video, or read more below.
## Creating a new Joystick
To create a new joystick, either find the Joystick Tool in the Stage Controls Tool menu, or by hitting the J key. Once the tool is activated, click anywhere on the stage to add a new Joystick.
If joysticks aren't visible on the stage, open the **View Options** menu and make sure **Joysticks** is enabled.
## Joystick Properties
With the Joystick selected, you’ll see a number of properties that you can use in the Inspector.
### Handle
The Handle X and Y property describes the position that the handle is currently in. You’ll notice that these properties update as you move the handle around the Joystick.
### Position
The position property describes the position of the Joystick on the stage. For the most part, we won't need to update this property at all while animating.
### Size
The size property describes the size of the joystick. We can modify this property to fit our needs by making the joystick longer/shorter or wider/skinnier.
### Draw in World Space
This toggle tells the joystick whether the joystick will scale with the zoom level or not. This option is useful when we need the joystick to always stay the same size, relative to the artboard.
### X & Y dropdown
The X & Y dropdowns allow us to assign timelines to the different axis of the Joystick.
For example, this timeline animates the 3D rotation of this ship. You'll notice many keys, which would make reusing this animation within other animations tricky to achieve.
When the timeline is assigned to the X axis of the Joystick, notice that the joystick becomes a slider that only allows you to move the handle in the X direction.
As we move the joystick up and down, you’ll notice that the ball also moves up and down. Keep in mind that we are scrubbing through the assigned timeline. We can now use this joystick to set keys on a new timeline to create animations.
**Invert Toggle**
After you add a timeline to an axis, you’ll notice a toggle that appears next to it. This allows you to invert the direction that the joystick scrubs through the timeline.
### Handle Source
The handle source allows you to constrain the position of the handle to another object's position properties. Use the button, then select an object you'd like to use as the handle's position source. This option is helpful when you want the mouse cursor to drive the joystick while the state machine is running.
## Joystick considerations
Joysticks are a powerful tool that allows you to create complex deformations, but like with many things in the Rive editor, there are a few things to keep in mind as you set up these controls.
### Conflicting properties
The most important consideration is that when you have multiple animations assigned to the joystick, those timelines will blend. If, for example, both timelines Y property of an object, these joysticks will conflict and prevent that object from moving in the Y direction.
Be sure to separate animated properties to prevent any conflicts. We now actively prevent you from creating keys for properties that are already being used in a timeline assigned to a joystick.
### Creating Complex Deformations
You can add as many keys to a joystick timeline as you’d like. By doing this, we can create incredibly complex deformations, but remember, these deformations will often bloat the size of your file, so use them sparingly.
# Manipulating Shapes Overview
Source: https://rive.app/docs/editor/manipulating-shapes/manipulating-shapes
The Rive editor gives you multiple ways to manipulate your graphics to create the animation that you want. In addition to adding the basic transform properties, you can use bones, meshes, joysticks, and constraints to give your graphics and animations some added flair.
} href="/editor/manipulating-shapes/meshes">
Meshes are an excellent way to add natural and organic deformations to raster graphics.
} href="/editor/manipulating-shapes/bones">
Bones allow you to create a skeleton for your graphics.
} href="/editor/manipulating-shapes/joysticks">
Joysticks make it easy to set up sophisticated rigs with simple controls.
} href="/editor/constraints/constraints-overview">
Constraints are a way to control the properties of an object through another target object.
# Meshes
Source: https://rive.app/docs/editor/manipulating-shapes/meshes
Add natural, organic deformations to raster graphics.
Meshes let you deform raster images by moving vertices instead of transforming the image as a flat rectangle. They are useful for natural motion, such as skin flexing, fabric rippling, hair flowing, or creating faux 3D effects.
A mesh is built from a [contour](#contours), which defines the mesh’s shape and vertices. Rive uses that contour to create a triangulated surface over the image.
## Adding a Mesh to an Image
Select an image on the Stage or Hierarchy and press **Enter** or go to the **Deform** section in the Inspector.
Click the **+** button and select **Mesh**.
## Deforming a Mesh
Enter Mesh Editing Mode by pressing `V` or by deselecting **Edit Contour**.
In mesh editing mode, moving a vertex changes the shape of any triangles connected to that vertex and deforms the pixels inside those triangles. Pixels in triangles that are not connected to the moved vertex are not affected.
Here’s a simple mesh with four vertices divided into two triangles:
Here’s a slightly more complex mesh with a center vertex, dividing the image into four triangles:
The position of these vertices can be keyed on a timeline or [controlled by bones](/docs/editor/manipulating-shapes/bones).
## Contours
The contour defines the visible shape and vertex structure of the mesh. Editing the contour changes the mesh structure without deforming the image.
### Creating Contours
When you add a mesh to an image, Rive automatically creates a simple contour with four vertices that make two triangles. You can use this contour as a starting point or make your own.
#### Manually Create a New Contour
Click the **New Contour** button to draw a new contour from scratch.
This is useful when the default contour does not match the shape of the image, or when you want full control over the mesh structure.
#### Auto-Trace a New Contour
Use **Auto-Trace** to quickly create a contour that follows the visible shape of an image.
Adjust the settings to control the generated contour:
* **Detail** - how closely the contour follows small details in the image.
* **Concavity** - how deeply the contour follows inward curves.
* **Uniform** - how evenly vertices are spaced along the contour.
* **Alpha** - the transparency threshold used to detect the image edge.
* **Padding** - extra space added around the traced outline.
#### Contour Bounds
Only the area inside the contour is rendered. Anything outside the contour is hidden.
This can be useful when you want to create multiple meshes from different parts of the same image.
### Editing Contours
Use **Edit Contour** or press **P** to adjust the existing contour. The Mesh Pen lets you add new vertices or move existing vertices without warping the image.
Click continuously to create forced edges between vertices. Press **Esc** between clicks to create separate vertices and let Rive automatically generate the edges between them.
#### Forced Edges
Forced edges are edges you manually add between mesh vertices. Use forced edges when you need more control over the mesh structure, such as defining an important seam, crease, corner, or area where the mesh triangles should follow a specific path.
When using the Mesh Pen, click continuously to create connected vertices with forced edges.
In this example, the default edges made the snake stretch in an unexpected way, creating a bump in its skin. By adding a forced edge, the edges run perpendicular to the body, helping the snake deform more naturally.
Forced edges are shown as thicker lines in the mesh.
To remove a forced edge, select **Edit Contour**, then **Cmd-click** the edge on macOS or **Ctrl-click** the edge on Windows.
### Generate
Use **Generate** to add more vertices to the current mesh. Click **Generate** multiple times to increase the mesh complexity.
A more complex mesh can create smoother deformations, but it can also be harder to edit.
### Resetting a Contour
Use **Reset** to return the mesh to its default contour.
## Managing Meshes
### Copying a Mesh
You can copy a mesh and apply it to another image.
Select the image with the mesh you want to copy and press **Cmd + C** on macOS or **Ctrl + C** on Windows.
Select the image you want to apply the mesh to and press **Cmd + P** on macOS or **Ctrl + P** on Windows to paste the mesh.
### Swapping Images
A mesh can continue to work when the image is changed.
This is useful when multiple images share the same basic structure. The same mesh can deform each face without creating a separate mesh for every image.
### Removing a Mesh
After an image has a mesh, the **Deform** section shows options for editing or removing it.
Select an image on the Stage or Hierarchy and press **Enter** or go to the **Deform** section in the Inspector.
Click the **Edit Mesh** or **Remove Mesh** button.
Removing a mesh deletes the mesh and its vertex edits from the image.
## Mesh View Options
Mesh view options control how the mesh and image appear while editing. These options can make it easier to focus on vertices, edges, weights, or the deformed result.
* **Bounding Box Edit** - show a bounding box around selected vertices so you can transform them together.
* **Isolate** - show only the graphic or graphics being edited.
* **Show Weights** - show mesh weights while editing.
* **Deform** - toggle between the deformed and undeformed version of the image.
* **Show Triangles** - show or hide the triangle edges. When disabled, only vertices are shown.
* **Dim** - dim the image so the mesh is easier to see.
# Solos
Source: https://rive.app/docs/editor/manipulating-shapes/solos
A Solo is similar to a group, but only one of the elements inside the solo is rendered at a time. This is much faster than having to animate the opacity of each object individually.
## Creating a Solo
There are two ways to create a solo. The first is to select multiple items on your artboard, right click them in the Hierarchy, and click "Wrap in Solo".
The second option is to select the **Solo** tool from the **Hierarchy Tools** dropdown (or click **S** on your keyboard) and click anywhere on your artboard. Now that you have a Solo in your Hierarchy, you can drag elements into it.
## Animating Solos
You can animate solos by opening a timeline and clicking the solo's radio buttons in the Hierarchy.
## Creating New Skins
One of the most common use cases for Solos is creating new skins for a character.

## Change the underlying rig
Sometimes when we animate an object or character, we need to create animations at different angles. Solos allow us to create multiple rigs and switch between them during our animation.

## Frame-by-frame animation
The ability to toggle between many images gives you a quick way to create frame by frame effects.

# Trim Path
Source: https://rive.app/docs/editor/manipulating-shapes/trim-path
The Trim Path feature allows you to draw only a portion of the stroke on a vector shape. This can be used to create a variety of animations where a line needs to follow a path. Every stroke you create for a shape can have its own independent Trim Path.

## Enable trim path
To activate Trim Path, select a shape that has a stroke and click the stroke options in the inspector. Now, use the Trim Path drop-down menu and select either Sequential or Synced mode. Both modes enable Trim Path, but behave differently when used on a shape with multiple paths.
### Sequential
When Trim Path is set to Sequential, paths are animated sequentially. The order in which they animate is dictated by their order under the shape.

### Synced
Synced mode animates the trim path along all paths concurrently.

## Start and end
The trim of a stroke happens from a Start point to an End point. By default, all shapes have a Stroke that starts at 0% and ends at 100%. Change these values to modify the position of the Start and End points of the trim (which are represented by a percentage of the full length of the path).

## Offset
Use Offset to easily move the trimmed portion of the path.

# Dashed Stroke
Much like Trim Path, the Dashed Stroke option allows you to dynamically change and animate parts of a path. Dash strokes allow you to customize the size of the dash and offset the dashes around the path. Note that you can add more than one dash size and gap to a path.

## Dash
The dash property controls the size of the dashed segments. This option can be in pixels, or a percentage length of the path.

## Offset
The offset property moves the dashes along the path. This option can be in pixels, or a percentage.

# Layers
Source: https://rive.app/docs/editor/state-machine/layers
Layers let you build more complex logic and animation with the state machine.
A state machine layer can play one state at a time. Add multiple layers when you want a state machine to play multiple animations or interactions at once.
For example, one layer might control a character's idle and walk animations, while another layer controls a separate hover or selected state.
## Creating a new layer
To create a layer, click the **+** button in the **Layers** tab.
## Layer Order
Layer order determines which layer takes priority when multiple layers animate the same property.
Layers farther down the list take priority over layers above them. In most cases, this does not matter. However, if multiple layers control the same object property, the animation in the lower layer takes priority as the layers mix.
### Changing layer order
To change layer order, drag layers up or down in the **Layers** tab.
## Layer Menu
Click the **...** button next to a layer name to open the layer menu.
| Option | Description |
| ------------- | --------------------------------------------------------------------- |
| **Delete** | Deletes the layer. |
| **Duplicate** | Creates a copy of the layer. |
| **Disable** | Disables the layer without deleting it. |
| **Snapping** | Enables or disables snapping for states and transitions in the layer. |
# Listeners
Source: https://rive.app/docs/editor/state-machine/listeners
Respond to interactions, property changes, and events in your Rive file.
Listeners let you respond to interactions, property changes, and other events in your Rive file. When a listener is triggered, it can perform actions such as updating property values, firing Rive events, or running other logic.
A listener has three main parts:
* [Target](#target): what the listener watches
* [Listen to](#listen-to): what needs to happen to trigger the listener
* [Action](#listener-actions): what happens in response
Use listeners to create dynamic experiences that react to user input and changes in state.
## Creating a Listener
Select the element you want the listener to respond to.
To listen for a view model property change or Rive event, select the artboard or nested component instead.
In the **Animations** panel, click the icon next to the **State Machine** to show listeners.
Click the **+** icon to create a new listener and select the action the listener should perform.
With the listener selected, set its **Target**, **Listen To**, and other options in the right sidebar.
With a listener selected, you’ll see its options in the right sidebar.
## Target
The **Target** determines what the listener watches. The target can be an element in your scene, an artboard, or a nested component.
Use an element as the target when you want to listen for interactions on that element, such as clicks or drags. Use an artboard or nested component as the target when you want to listen for view model property changes, Rive events, or other events that belong to that artboard or component.
### Opaque Target
**Opaque Target** controls whether pointer events stop at this listener's hit area or continue through to elements behind it.
When **Opaque Target** is enabled, pointer events stop at the target. When it's disabled, pointer events can pass through the target and trigger other listeners underneath.
## Listen To
**Listen to** is the condition the listener watches for. When the condition is met, the listener is triggered.
| Listen To | Description |
| -------------------------------- | -------------------------------------------------------------- |
| **View Model Property Change**\* | Fires whenever the selected view model property changes.\*\* |
| **Rive Event**\* | Fires when a Rive event is fired. |
| **Pointer Enter** | Fires when the pointer enters the target area. |
| **Pointer Exit** | Fires when the pointer leaves the target area. |
| **Pointer Move** | Fires repeatedly while the pointer moves over the target area. |
| **Pointer Down** | Fires when the pointer is pressed down on the target. |
| **Pointer Up** | Fires when the pointer is released over the target. |
| **Click** | Fires when the target is clicked or tapped. |
*\* Available when the listener target is an artboard or nested component.*
*\*\* View Model Property Change listeners fire whenever the selected property changes. You can’t specify an expected value or direction of change. For example, a Boolean listener fires when the value changes from true to false or from false to true.* **Pointer Exit** only fires while Rive can read the pointer position. If the target touches the edge of the canvas or container, moving the pointer out of the container may not trigger **Pointer Exit** because the pointer has left the area Rive is tracking.
We strongly recommend using [Data Binding](/docs/editor/data-binding/overview) to communicate between artboards instead of relying on nested Rive events.
## Listener Actions
A listener action runs when a listener is triggered.
Actions include:
* Changing a view model property
* [Aligning an element to a target](#align-target)
* Reporting an event
* Triggering a scripted action
* Firing a Rive event
* Updating an input value (deprecated)
To add a listener action, click the **+** icon in the panel below the **State Machine Graph**.
You can add multiple actions to a single listener.
You can data bind values in listener actions.
### Align Target
The **Align Target** action positions an object so it follows the pointer when the listener is triggered within the listener area.
Use the **Target Picker** to select the object you want to align.
Enable **Preserve Offset** to maintain the original distance between the object and the pointer when the action is triggered. When **Preserve Offset** is disabled, the object aligns directly to the pointer.
# State Machine Overview
Source: https://rive.app/docs/editor/state-machine/state-machine
Add intelligence to your animations.
## Overview
State Machines are a visual way to connect animations together and define the logic that drives the transitions. They allow you to build interactive motion graphics that are ready to be implemented in your product, app, game, or website.
State machines create a new level of collaboration between designers and developers, allowing both teams to iterate deep in the development process without the need for a complicated handoff.
Using the State Machine requires designers and animators to think more like a developer but in a straightforward, visual way.
Every artboard has at least one State Machine by default, but you can create as many as you’d like. To create a new state machine, hit the plus button in the Animations List and select the State Machine option.
### Anatomy of a State Machine
A basic state machine will consist of a Graph, [States](/docs/editor/state-machine/states), [Transitions](/docs/editor/state-machine/transitions), and [Layers](/docs/editor/state-machine/layers). We’ll explore each of these pieces and more throughout this section.
The Graph is the space in which you’ll be adding States and connecting Transitions. It appears in place of the Timeline when a state machine is selected in the animations list.
States are simply timeline animations that can play in your state machine. Typically, these will represent some state that your animated content is in. For example, a button will typically have an Idle state (the button is stationary), a Hovered state (what the button looks like when it is hovered), and a Clicked state (what the button looks like when it’s been clicked).

Once we have defined the States of our content, we can tie them together with transitions to create a logical path that our State Machine can take through these different timelines. We’re creating a map that our State Machine can use to get from one animation to the next.

# States
Source: https://rive.app/docs/editor/state-machine/states
States are simply timeline animations that can play at any point in your state machine. A state could be as simple as changing the color and position of an object, or as complex as blending multiple timelines together.
There are a few types of states that you’ll end up using as you work with the State Machine, including Default States, Single animations, and Blend States. We’ll explore each of these below.
## Default States
The Default States are the states that, by default, are added to every State Machine.
### Entry State
The Entry State is the state that your State Machine will start from. You’ll notice that by default, your state machine will already have an animation attached to the Entry State, but you can change this animation at any time. Note that you can connect multiple animations to the Entry State if you need I.E. you want to build a switch that can start in either the on or off state.

### Exit State
The Exit State tells the State Machine layer to stop playing. This niche state has uses when multiple layers are being used.
### Any State
Unlike normal states, states connected to the Any State can be played at any time, regardless of which state your state machine is in. Any States are great to use when you want to create an array of states that can be activated at any time, such as changing the skin of a character.

## Animation States
Animation states include all states other than the default states added to a State Machine. These states will control the look and motion of your interactive content. There are three types of animation states; Single Animation, 1D blend, and Direct blend states.
To add a State to the Graph, you can drag and drop an animation from the Animations List directly onto the Graph. Notice that this will create a Single Animation state. You can change the state type using the inspector.

Additionally, you can right-click on the graph and create a blank state of any type with no associated timelines.

Right-click to add State
To assign a timeline to a state, use the timeline dropdown in the inspector.
### Single animation state
Any timeline that we create can be used as a single animation state. Depending on the type of animation we are using, the single animation state could be a one-shot, looping, or ping-pong state. In most cases, you’ll be using single animation states to create most of your state machines.
### Blend states
A Blend State is any state that blends together two or more timeline animations. We use these states for content like loading bars, health systems, scrolling interactions, and dynamic face rigs.
There are two types of blend states; 1D and Direct Blend states.
#### 1D Blend state:
A 1D Blend State allows us to mix multiple timelines together with a single numerical view model property. This state works by ramping up one animation and ramping down the other while you increase or decrease a number value. Note that this mixing is not linear, but is additive and could give you unexpected results.

**Configuring a 1D Blend State:**
You'll want to start by creating a few timelines for your Blend state. Keep in mind that it's often best to use timelines with only a few properties keyed. In this health bar example, only the X scale is keyed.

Timelines for health bar
After adding a 1D Blend State to the graph, use the Inspector to configure the state.

First, add the number property you want to drive the blend using the dropdown. If you haven’t created one yet, you’ll notice that nothing appears here.

The plus button that appears below the number property allows you to add timelines to your blend state. Use the dropdown to assign a specific timeline. Note that you can add as many timelines as you’d like.

Next, you need to define a numerical range that your blend state will work between. This particular blend works between 0 and 100.

Notice that once you define the range, a graphic appears above the property dropdown, visually representing how your animations will mix. When the state machine is active, as you increase or decrease your property within the defined range, you’ll see a visual representation move across that graph, showing you the mix of your timelines.

#### Additive Blend state:
An Additive Blend state allows you to blend together multiple timelines using multiple number properties. This allows us to create unique poses and facial positions by mixing multiple animations together. While working with an Additive Blend, you’ll either be mixing an animation by value or property. Read more below.

**Value vs Property blend**
When adding animations to an Additive blend state, you’ll be prompted to either add a Blend by Value animation or a Blend by Property animation.

A Blend by Value timeline can be thought of as the baseline animation, or default pose. This value is not tied to a property, so it can’t be used to control the state machine. Instead, this value describes its mix weighting.
A view model property blend is an animation that is mixed with the default pose or motion via a number property. Each of your different property blends should have their own number property.
## State Options
When you select a state in the State Machine Graph, you can change its options in the Inspector, including:
* **Name** - The name of the state.
* [Caption](#caption) - Notes for collaborators or your future self.
* **Type** - The type of state, such as a single animation, 1D blend, or additive blend.
* [Timeline](#timeline) - The animation assigned to the state.
* [Speed](#speed) - The speed at which the timeline plays.
* [Transitions](#transitions) - A list of transitions to and from the state.
### Caption
You can add a caption to a state to leave notes for collaborators or your future self.
When a state has a caption, an info icon appears on the state in the State Machine Graph. Hover over the icon to view the caption.
Captions are useful for explaining why a state exists and documenting complex logic.
Captions are only visible in the editor. They are not exported for runtime.
### Timeline
Use the dropdown to change which animation is assigned to the current state.

### Speed
Use **Speed** to change how fast the state’s animation plays. Positive values play the animation forward, and negative values play it backward.

### Transitions
The **Transitions** section lists the [transitions](/docs/editor/state-machine/transitions) connected to the selected state. Use the eye icon to disable a transition, or the **-** button to remove it.
## Actions
You can perform actions when the state starts or ends.
Actions can be used to:
* Set property values
* Report events
* Align targets
* Control focus
* Fire a scripted action
Actions can also be fired at the start or end of a transition.
See [Transition Actions](/docs/editor/state-machine/transitions#actions).
### Action Timing
Each action can run at either:
* **Start** — Runs when the state begins
* **End** — Runs after the state completes
### Creating an Action
With a state selected, click the `+` button to the right of the state name, and select an action type.
Configure the action based on the action type.
## Common Issues
## Use case: Build a simple button
In this exercise, we will use our state machine knowledge to create a simple button with two layers of interactivity. Hover and click.
# Transitions
Source: https://rive.app/docs/editor/state-machine/transitions
Transitions define how and when a State Machine moves from one state to another. A transition is made up of four parts:
The direction the transition travels.
**Example**: A character might have one path from **Idle** to **Walk**, and another from **Walk** back to **Idle**.
When the transition occurs.
**Example**: A character might transition to **Walk** when a button is pressed, then back to **Idle** when the button is released.
How the transition behaves.
**Example**: You might set the transition duration to `0.2` seconds so the change between states feels smoother.
Extra behavior to perform when the transition occurs.
**Example**: A transition might fire an event, set a property value, or align a target.
## Transition Path
A transition path determines how the State Machine moves from one state to another.
To create a transition path, move your cursor near the state you want to leave until the circle appears. Click and drag from the circle to the state you want to transition to.
Once you connect two states, the transition path shows a circle with an arrow icon indicating the transition direction. In this example, the State Machine can move from Timeline 1 to Timeline 2, but not from Timeline 2 back to Timeline 1.
To allow the State Machine to move back to Timeline 1, create another transition path starting from Timeline 2.
You can create multiple transitions between the same two states. Each transition can have different [conditions](#transition-conditions), allowing you to create “or” logic.
## Transition Conditions
Conditions determine when a transition can occur.
Without conditions, transitions fire as soon as they are reached. This can cause a State Machine to rapidly move between states or skip directly to a single animation.
### Adding a Condition
To add a condition to a transition, select the transition, then click the plus button next to **Conditions**.
A condition has three parts:
* **Source value** - the value or event the condition watches
* **Operator** - how the source value is compared
* **Comparison value** - the value the source value is compared against
For example, a character might transition from **Idle** to **Walk** when `isWalking` is `true`.
In this condition, `isWalking` is the **source value**, **equals** is the **operator**, and `true` is the **comparison value**.
#### The Source Value
Conditions can be driven by many different sources, including:
* View Model properties
* Events
* Built-in properties such as artboard width, height, and ratio
#### The Operator
Conditions can use comparisons such as equals, does not equal, greater than, greater than or equal to, less than, and less than or equal to. The available comparisons depend on the type of value being compared.
#### Comparison Value
The comparison value is the value the source value is compared against.
You can enter a fixed comparison value, such as `true`, `100`, or `"mobile"`. You can also data bind the comparison value to another property, allowing the transition to compare against dynamic data.
For example, a transition might occur when `speed` is greater than a data-bound `walkThreshold` value.
### Adding Multiple Conditions
You can add multiple conditions to a single transition. All conditions must be met before the transition occurs.
## Transition Properties
Once you’ve added a transition, selecting the direction indicator will allow you to configure the transition. There are three different sections to the transition panel, the transition properties, conditions, and interpolation.
Selecting multiple transitions allows you to update multiple properties at once.
### Duration
The duration property describes how long it takes for a transition to occur.
The duration is set to zero by default, meaning the transition happens immediately. So, when we transition between these two animations, it appears as though the object snaps from one side of the artboard to the other.
### Exit Time
Exit Time tells the state machine how much of the state must play before transitioning.
By default, Exit Time is unchecked. To enable Exit Time, use the checkbox. Once the setting is enabled, you can use either a time value or percent.
For example, if you want the state machine to play the entire animation before transitioning, you can either enter the duration of the animation, or use 100%.
### Pause Source When Exiting
**Pause Source When Exiting** pauses the animation in the state you are leaving while the transition plays.
In this example, the up-and-down motion pauses as soon as the transition begins.
### Allow Exit During Transition
**Allow exit during transition** controls whether the State Machine can leave a transition before that transition finishes.
For example, if a button is transitioning from **Idle** to **Hover** and the user moves their cursor away, **Allow exit during transition** lets it transition back to Idle immediately.
### Interpolation
You can add interpolation to your transition at the bottom of the Transitions Panel. By default, the interpolation is set to linear, but you can use the cubic and other interpolations.
If you are unfamiliar with the basics of Interpolation, see [Interpolation (Easing)](/docs/editor/animate-mode/interpolation-easing).
## Actions
Transitions can also perform actions when the transition occurs.
Unlike conditions, which determine whether a transition can happen, actions perform an operation when the transition is triggered.
Actions can be used to:
* Set property values
* Report events
* Align targets
* Control focus
* Fire a scripted action
Actions can also be fired at the start or end of a state.
See [State Actions](/docs/editor/state-machine/states#actions).
### Creating an Action
With a transition selected, go to the Actions tab, click the `+` button, and select an action type.
Configure the action based on the action type.
#### Action Timing
Each action can run at either:
* **Start** — Runs when the transition begins
* **End** — Runs after the transition completes
## Randomize Exit
**Randomize Exit** lets a state choose from its outgoing transition paths at random.
To use it, select the origin state and enable **Randomize Exit**. The state lists each path leaving it and lets you assign a weight to each path.
The higher the weight, the more likely that path is to be chosen. For example, if **Timeline 1** has a weight of `3` and **Timeline 2** has a weight of `1`, the State Machine has a 75% chance of transitioning to **Timeline 1** and a 25% chance of transitioning to **Timeline 2**.
## Disabling a Transition
You can temporarily disable a transition without deleting it. Disabling is useful when you want to isolate part of a state machine for testing, or temporarily remove a path without losing its configuration.
There are two ways to toggle a transition's enabled state:
* **From the transition panel:** select the transition and click the enable/disable icon in the panel.
* **From the canvas:** right-click the transition and choose **Disable transition** (or **Enable transition** if it's already disabled).
# Tagging
Source: https://rive.app/docs/editor/tagging
Tagging is a way to organize your hierarchy further. Create tags, then apply them to objects in the Hierarchy like Bones or Groups, then filter the view to see exactly what you need, when you need it.
## Creating Tags
There are two ways to create a Tag: either through the Hierarchy or by using the Inspector. Remember that any Tag created can be used on any artboard in the file.
### In the Inspector
To create a new tag, ensure that you have nothing selected on the artboard, then use the plus button next to the Tags option.

Once the Tag has been added, you can change the name and color to your liking.
### In the Hierarchy
There are two ways to create tags through the Hierarchy. The first is by using the tag menu at the top of the Hierarchy. From here you can create new Tags, edit Tags, collapse Tags, filter, lock, or select assigned Tags.

The second way is to select one or more objects to create a tag directly in the hierarchy, then right-click and use the add tag option. From there, you can either create a new tag or assign a tag that you’ve already created.

To edit the Tag's properties, use the steps above.
## The Tags Menu
The Tags Menu is where you will find most of the Tag Options. Some of these options are self-explanatory, such as creating or editing a tag. Let’s explore some of the other available options.
### Collapse Tag
When you add a tag to an object, you’ll notice that the Tag's name and the color pip are displayed to the right.

We can use the Collapse Tag option to hide the name. Conversely, we can use the Reveal Tag option to display the names again.
### Filter Tags
When you mouse over a tag in the menu, you’re given the option to Filter. This option will filter your Hierarchy and only show you the objects with the filtered tag. The current filtered tabs are shown next to the Tag Menu.

Note that you can add as many tags to the filter as you want. This is a great way to show only the controls you need to create an animation.
### Locked
The Locked option will allow you to lock the objects with the selected Tag. When locked, you can no longer select that object on the stage.

This is a great tool to use when you want to hand off a file to another animator so that they don’t inadvertently use a control that shouldn’t be.
This option is also available via the Inspector via the lock icon.
### Select
The Select option will select every object with the assigned tag.
Note that this option is also available in the Inspector via the target icon.
# Font Assets
Source: https://rive.app/docs/editor/text/fonts
Import and customize fonts.
## Importing Fonts
See [Importing Assets](/docs/editor/fundamentals/assets-overview#importing-assets) for information about importing files, including TTF and OTF files.
### Google Fonts
You can add Google Fonts directly from the text style font options. Select a text run, choose a Google Font, and Rive adds the font to the Assets panel automatically.
## Export Options
Font assets include an Include option that controls which glyphs are included when the font is exported.
* **All glyphs**: Include the full font.
* **Glyphs used**: Include only the glyphs used in the file.
* **Custom**: Include a custom set of glyphs.
Fonts can significantly increase the size of your exported `.riv` file, especially when exporting **All glyphs**. Use **Glyphs used** or **Custom** when you only need a smaller set of characters.
To control which glyphs are included, create an artboard that you do not export to the `.riv` file. Add a text run with the exact characters you want to include, then set the font asset’s **Include** option to **Glyphs used**.
For example, if your file only needs numbers, add a text run with `0123456789`. Those glyphs will be included in the exported font.
### Custom - Export Scripts
When **Include** is set to **Custom**, you can use **Export Scripts** to choose which glyph groups to include, such as Latin, Thai, or other writing systems.
# Text Modifiers
Source: https://rive.app/docs/editor/text/text-modifiers
Text Modifiers let you animate or transform individual characters, words, lines, or glyphs within a text object.
By combining ranges, falloff, and animated offsets, you can create effects such as text reveals, wave animations, bouncing characters, and per-letter motion.
## Modifier Group
A Text Modifier Group combines a **Range** and a set of **Properties**.
* **Properties** determine what changes, such as position, rotation, scale, opacity, or follow path.
* The **Range** determines which characters, words, or lines are affected.
A single modifier group can affect multiple properties that share the same range.
Multiple modifier groups can be combined to create layered effects. For example, one modifier group might animate character scale while another animates position.
### Adding a Modifier Group
Select a text object, then click the `+` button in the **Text Modifier** panel.
### Properties
Properties determine how the text is modified.
Each modifier group can modify one or more of the following properties:
* **Position** - Move text along the X and Y axes.
* **Rotation** - Rotate text around its origin.
* **Scale** - Change the size of the text.
* **Origin** - Adjust the pivot point used for rotation and scale.
* **[Opacity](#opacity)** - Change the transparency of the text.
* **[Follow Path](#follow-path)** - Align text to a path and control how glyphs follow its direction.
* **Variables** - Adjust variable font axes such as weight or width. Requires a variable font that supports the selected axis.
Multiple properties can be combined within a single modifier group to create more complex effects.
#### Adding a Property
Select a text object, then click the `+` button in the **Modifier Group**. Each property has its own settings, which can be adjusted and animated.
If text modifier range or values aren't visible on the stage, open the **View Options** menu and make sure **Text Modifier Range** **Text Modifier Values** are enabled.
#### Opacity
The Opacity value controls the opacity of text within the modifier group's range.
The Invert option controls the opacity of text outside the range:
* Disabled: Text outside the range is fully transparent (0% opacity).
* Enabled: Text outside the range is fully visible (100% opacity).
#### Follow Path
To make non-text objects follow a path, use the [Follow Path Constraint](/docs/editor/constraints/follow-path-constraint) instead.
The Follow Path modifier allows text to follow the shape of a vector path. This can be used to create curved text, circular text layouts, or animated text that moves along a path.
* **Follow** - The path to follow.
* **Trim Start and End** - Defines where text begins and stops following the path. Text outside the trimmed section continues from the nearest trim point in the direction the path is facing.
* **Offset** - Where along the path to start.
* **Strength** - Controls how strongly the text follows the path. At 0%, text stays in its original position. At 100%, text follows the path completely. Animate Strength to transition text onto or off of a path.
* **Auto Orient Glyphs** - Make the rotation of the glyph follow the path.
* **Auto Orient Lines** - Rotates each line to follow the direction of the path.
### Range
The range can be defined as a percentage of the text length or as an index value.
Ranges can be applied by character, word, or line. Open the Range Options fly-out to configure the range behavior.
A range with a start value of 0% and an end value of 50% affects only the first half of the text. If the modifier group applies a scale of 25%, only glyphs within the first half of the text will be scaled.
You can see a visual representation of the range via a shaded area on the stage. You can configure the stage visual options in the visibility menu on the toolbar.
Multiple ranges can be added to a single modifier group to create complex effects.
#### Run
Determines whether the modifier is applied to the entire text object or only to a specific text run.
When a text object contains multiple text runs, modifiers can be limited to a single run instead of affecting all text.
#### Falloff
Use the falloff values to add interpolation to the applied modifier properties. For example, a modifier group that scales glyphs to 200% with a range from 0% to 100% and a falloff of 25% to 75% will gradually scale glyphs up from 100% to 200% over the first quarter of the text, and back down again over the last quarter of the text.
The falloff can be visualized via the darker shading on the stage guide.
#### Offset
Use Offset to move the range along the text. Try animating the offset value to create wipe effects along a text value. For example, a scale modifier can animate the offset to make individual glyphs scale up and back down again.
#### Range Options
Additional range options can be found in the fly-out alongside the range name for more fine-grained control over the range selection behavior.
**Increment**
Define whether modifier properties get applied by:
* Characters (with or without spaces)
* Words
* Lines
The increment value will affect an index-based Range Type.
**Mode**
Defines how modifier values are combined when multiple ranges overlap.
For example, if two overlapping ranges each apply a scale of 200%, an **Add** mode would combine them to produce a scale of 400%.
Turn on Modifier Range Values in the visibility menu to get a numerical indication of how much a Text Modifier is affecting glyphs.
**Strength**
Adjust the overall influence of the modifier within the range. A value of 0% disables the modifier, while 100% applies its full effect.
**Range Type**
Set the range type to configure range start, end, and offset values as a percentage of the text length, or by indices. You may also want to consider which increment value to use alongside the Range Type. For example, an index value of 1 incremented by characters will target the second character, whereas an index value of 1 incremented by words will target the start of the second word.
**Falloff Interpolation**
Modify the falloff interpolation to define a custom cubic curve. The falloff defaults to linear interpolation.
## View Options
Toggle visual guides on the stage via the visibility menu in the toolbar:
* Text Modifier Range: Toggle the display of a shaded area on the stage to highlight the range position on a selected text object.
* Text Modifier Range Values: Toggle the display of per-glyph values on modifier ranges to indicate the applied strength of a given modifier.
## Use case: Animating a text pendulum
This simple example will get you used to animating with Text Modifiers.
# Text Overview
Source: https://rive.app/docs/editor/text/text-overview
## Text Guides
} href="/editor/text/text-runs">
Runs allow you to break your text up into sections — typically, they're used to apply a variety of styles to a single block of text.
} href="/editor/text/text-styles">
A Text Style contains many of the familiar options that define how you'd like your text to be styled, and is applied to one or more runs.
} href="/editor/text/text-modifiers">
Modifiers provide powerful ways to manipulate and animate the glyphs that make up text. Currently, the available Text Modifier properties include:
} href="/editor/text/fonts">
Select a font in the inspector as part of a Text Style.
# Text Runs
Source: https://rive.app/docs/editor/text/text-runs
Runs allow you break your text up into sections — typically, they're used to apply a variety of styles to a single block of text. Whilst most tools manage text runs behind the scenes, Rive exposes them for greater control when dynamically changing text at runtime.
You may want to split your text into multiple runs to apply a different style (such as font, font size, color etc.) to a certain part of your text, where you can then [update your text runs at runtime](/docs/runtimes/data-binding).
A Text Run may only have one Text Style applied at a time.
For example, an animation welcoming a user to an app or website may greet them by their name. In the Rive Editor, you may design and animate the text to read "Welcome back, username". Defining "username" as its own run means you can target it with the Rive Runtimes and replace it with the user's name.
***
## Creating a Text Run
To create a Run, select the desired portion of text and select the 'Run from Selection' button in the Inspector. You can see Text Runs listed beneath the text object in the hierarchy.
Double click or press `Enter` with the text box selected to start editing text.
Toggle the 'Highlight Text Runs' option in the inspector for a visual guide of your current Text Runs. Hovering a run in the hierarchy also highlights its location within the text.
***
## Managing Text Runs
Select a Text Run in the hierarchy for inspector options:
* **Text Value:** Update the text value for the run. Key this value in [animate mode](/docs/editor/fundamentals/design-vs-animate-mode).
* **Edit Text Run:** Initiate the text editor with the run pre-selected.
* **Merge with Next:** Combine the selected run with the next run.
* **Merge with Previous:** Combine the selected run with the previous run.
* **Delete text Run:** Delete the run and its contents.
* **Style:** Assign one of the Text Styles defined on the text object. Key this value in [animate mode](/docs/editor/fundamentals/design-vs-animate-mode).
# Text Styles
Source: https://rive.app/docs/editor/text/text-styles
A Text Style contains many of the familiar options that define how you'd like your text to be styled, and is applied to one or more runs. Currently, only Text Styles defined on a Text object can be applied to it. We'll introduce ways to share Text Styles across multiple Text objects or entire Rive files in future.
Each new Text object is created with a Text Style. Use the `+` action in the Inspector to create additional styles to be applied to specific Text Runs or to key between in an animation.
Each Text Style contains:
* Font
* Font Size
* Font Weight
* Line Height
* Fills
* Strokes
### Applying a style to a Text Run
There are two ways to apply a Text Style to a Text Run:
* Select the Text Run in the hierarchy, then use the Style dropdown in the Inspector.
* Select the `A+` icon alongside the style options in the Inspector, then use the popup menu to select the desired Text Run. Hovering each option will preview the result on the Stage.
***
## Variables
Fonts that support variable axes or OpenType features will surface an options fly-out button on the Text Style within the Inspector. Use the fly-out to access and configure the available variables and features for the selected font.
Font variables can be animated in Rive. Open the variable fly-out in [animate mode](/docs/editor/fundamentals/design-vs-animate-mode) to key the available axes.
# Dependencies Graph
Source: https://rive.app/docs/editor/workflows/dependency-graph
Use the Dependency Graph to visualize relationships between elements in your file
## Show Dependencies
To show dependencies, select an item and press **D**. Rive displays arrows connecting the selected item to its parent and child dependencies.
Dependency arrows are color coded based on the type of relationship.
For example, if the rotation of `NEW_sticker` is controlled by the rotation of `Icon` through a constraint, Rive shows that dependency with a yellow arrow.
## Filtering
Use the dependency options to filter which relationships are shown. This is helpful when an element has many connections and you only want to focus on specific dependency types.
# Feature Support
Source: https://rive.app/docs/feature-support
As the Rive Editor evolves, some new features require updates to the Rive runtimes. In certain cases, this may introduce new or modified APIs. We recommend staying on the latest runtime version to ensure compatibility, bug fixes, and performance improvements.
Use the table below to verify whether a feature used in your `.riv` file is supported by your target runtime.
Certain features require the use of the Rive Renderer at runtime. See our
documentation on [choosing a renderer](/docs/runtimes/choose-a-renderer/).
Currently, the only feature that requires the Rive Renderer is **[Vector
Feathering](https://rive.app/blog/introducing-vector-feathering?utm_source=docs\&utm_medium=content)**.
When a feature requires API changes, migration notes will be included below.
## Feature Support by Runtime
### Runtimes
Choose between @rive-app/webgl2 and @rive-app/canvas, with guidance on performance, package size, and when to use canvas-lite.
For better performance and the latest features, like vector feathering, we recommend using the WebGL2 runtime, which uses the Rive Renderer.
### Lite Runtimes
This lightweight version uses the same API as `@rive-app/canvas`, but excludes certain features to reduce bundle size.
This lightweight version uses the same API as `@rive-app/react-canvas`, but excludes certain features to reduce bundle size.
### Legacy Runtimes
The `@rive-app/webgl` runtime is deprecated. For better performance and the latest features, use `@rive-app/webgl2`.
The `@rive-app/react-webgl` runtime is deprecated. For better performance and the latest features, use `@rive-app/react-webgl2`.
The `rive-react-native` runtime is deprecated. For better performance and the latest features, use `rive-nitro-react-native`.
[Migration guide](/docs/feature-support#react-native-legacy)
## Runtime Support by Feature
A green checkmark (✅) indicates that a feature is supported in all current runtimes.
A yellow circle (🟡) indicates that support varies by runtime or renderer.
Differences may reflect:
* Platform or SDK limitations where a feature cannot be supported
* Staggered releases as features roll out across runtimes, or
* Lightweight (“lite”) builds that intentionally omit some features to reduce package size.
A feature may still be considered fully supported even if it is unavailable in legacy runtimes.
### Features
Accessibility voice over support with [Semantics](/docs/editor/accessibility/semantics).
Support for Rive files with [Scripting](/docs/scripting).
Data binding lists, images, and artboards were added after initial data binding support.
See [Data Binding Overview](/docs/editor/data-binding/overview) and [Data Binding for Runtimes](/docs/runtimes/data-binding).
See [Data Binding Overview](/docs/editor/data-binding/overview) and [Data Binding for Runtimes](/docs/runtimes/data-binding).
See [N-Slicing](/docs/editor/layouts/n-slicing).
Allows Rive to automatically update the artboard size as the underlying view/canvas/widget/texture size changes. See [Layouts](/docs/editor/layouts/layouts-overview).
Allows Rive to use a fallback font if a glyph is not available. A default font is automatically chosen, or you can optionally configure the desired fallback font based on various options. See [Fallback Fonts](/docs/runtimes/text#fallback-fonts).
Enables randomizing transitions between animations and customizing the probability.
See [Rive Events](/docs/runtimes/rive-events) and [Audio Events](/docs/editor/events/audio-events).
See [Loading Assets](/docs/runtimes/loading-assets).
See [Text](/docs/runtimes/text).
### Legacy Features
Listening to [Rive Events](/docs/runtimes/rive-events) at runtime is deprecated and will be removed in future versions.
Use [Data Binding](/docs/runtimes/data-binding) to listen for triggers or changes to properties instead.
Setting text, including [nested text](/docs/runtimes/text#read-update-nested-text-runs-at-runtime), at runtime is deprecated and will be removed in a future version.
Instead, use [Data Binding](/docs/runtimes/data-binding) to update a string, which is bound to a text run.
# Defold
Source: https://rive.app/docs/game-runtimes/defold
[Defold](https://defold.com/) is a free, cross-platform, game engine that has built-in support for Rive graphics, allowing you to easily incorporate Rive into your Defold games.
Rive integration is managed by the Defold team.
For more information on using Rive with Defold, please refer to the [official Defold documentation](https://defold.com/extension-rive/). There, you will find comprehensive guides on setting up Rive graphics and best practices for using them within the Defold engine.
### Rendering
Defold has integrated the [Rive Renderer](https://rive.app/renderer) natively, which means that Rive graphics in Defold are rendered by the same renderer used in the Rive Editor. This native integration offers several benefits:
* **Performance**: The Rive Renderer is custom-built for Rive content, for animation, and for runtime. Allowing you to draw an unprecedented amount of vector graphics with mega fast rendering. [Read more](https://rive.app/blog/rive-renderer-now-open-source-and-available-on-all-platforms).
* **Quality**: Graphics remain crisp and clear regardless of scale, resolution, or device.
* **Consistency:** Your graphic will look exactly the same in the Editor and at Runtime.
* **Feature support:** You'll also benefit from upcoming Rive features, such as blurs and shadows, that will only be possible through the Rive Renderer.
### Support
If you have any questions about Rive in Defold, feel free to explore their [community forums](https://forum.defold.com/) or reach out to their support. If you believe the issue to be with Rive, reach out to us on [our Community](https://community.rive.app/c/support/).
# Audio
Source: https://rive.app/docs/game-runtimes/unity/audio
Rive audio playback in Unity.
In the Unity runtime, audio playback from Rive files is automatically routed through Unity's audio system via the `AudioProvider` component, which wraps an `AudioSource` to mix Rive's audio output into the Unity audio pipeline.
Audio playback via `AudioProvider` is **not supported in WebGL builds**. On WebGL, Rive automatically falls back to system audio instead of routing through Unity's `AudioSource`.
## How it works
Each `RiveWidget` that plays a Rive file with audio needs access to an `AudioProvider`. By default, all widgets share a single **global** `AudioProvider` that is created automatically at runtime. No additional setup is required.
If you need more control (for example, to manage volume or audio mixer groups independently per widget), you can assign a **custom** `AudioProvider` to individual widgets.
## Global Audio Provider
The global `AudioProvider` is created automatically the first time a widget with audio needs it. It is shared across all `RiveWidget` instances that don't have a custom provider assigned.
This is the recommended setup for most projects, and no additional configuration is required.
## Custom Audio Provider
To use a custom `AudioProvider` for a specific widget:
Add an `AudioProvider` component to a GameObject in your scene. The `AudioProvider` component requires an `AudioSource` on the same GameObject. Unity will add one automatically.
Assign the `AudioProvider` to the **Custom Audio Provider** field on your `RiveWidget` in the Inspector.
You can also assign a custom provider at runtime via script:
```csharp theme={null}
[SerializeField] private RiveWidget m_riveWidget;
[SerializeField] private AudioProvider m_audioProvider;
private void Start()
{
m_riveWidget.CustomAudioProvider = m_audioProvider;
}
```
Setting `CustomAudioProvider` to `null` reverts the widget to using the shared global provider.
## Platform Notes
| Platform | Behavior |
| ------------------- | -------------------------------------------------------------------------------------- |
| Editor / Standalone | Audio routed through Unity's `AudioSource` via `AudioProvider` |
| iOS / Android | Audio routed through `AudioSource`; `Play()` is called automatically on start |
| WebGL | System audio is used; `AudioProvider` is not supported and will log a warning if added |
Adding an `AudioProvider` component to a WebGL build will produce a warning at runtime. The component has no effect on that platform, and audio will still play via the browser's system audio.
## AudioProvider component reference
| Property | Description |
| ------------- | -------------------------------------------------------------------------------------------------------- |
| `AudioSource` | The `AudioSource` component used for audio playback. Required; automatically added with `AudioProvider`. |
# Best Practices
Source: https://rive.app/docs/game-runtimes/unity/best-practices
Performance and usage considerations for Rive in Unity.
Rive is designed to be efficient in Unity, but performance can still be affected by core factors like how much work happens every frame, how much memory is in use, and how much rendering overhead is required.
Design-time and runtime guidance that applies across platforms.
Learn how Rive Panel, Rive Widget, and render target strategies work.
## Rive Panels and Render Textures
In Unity, **one Rive Panel renders into one Render Texture** by default. Every **Rive Widget** under a panel draws into that same Render Texture.
That's why, for UI:
* **Prefer fewer panels**: One (or a small number) of panels is usually best, especially on mobile.
* **Group widgets under a shared panel**: Multiple widgets under a single panel is typically more efficient than multiple panels.
### Compose UI in Rive
When you're getting started with Rive, it can be tempting to use it as a simple replacement for individual UI components (for example, building a single “Rive Button”, instancing it many times, and then assembling it in uGUI/UIToolkit). In Unity, this often creates unnecessary widget/panel/render texture overhead.
For most projects, the recommended approach is to build your full menu (or at least larger chunks of a screen) in Rive, render it single widget/panel, and drive it with data binding.
### Prefer data binding with lists for dynamic content
If your UI needs to create and remove items dynamically (for example: inventories, chat feeds, spawning characters), prefer using **data binding list properties** instead of creating lots of separate Rive Widgets.
Using lists keeps repeated graphics **inside a single Rive file / widget**, while still letting you add/remove/swap items at runtime through your Unity code.
See:
* Concepts + examples: [Data binding lists](/docs/game-runtimes/unity/data-binding#lists)
* Editor setup: [Lists in the editor](/docs/editor/data-binding/lists)
### Screen space UI vs world space UI
* **Screen space UI**: You can usually structure your UI so a single panel covers the whole screen.
* **World space UI / VR**: If your UI exists in the world and needs different sizes, transforms, or visibility rules, you may end up needing more panels and more careful control of render textures.
If you have a real use case for many panels (for example, lots of world-space UI surfaces), look at **Render Target Strategies**:
* **Atlas Render Target Strategy**: Packs multiple panels into a shared atlas texture to reduce texture count.
* **Pooled Render Target Strategy**: Reuses textures to reduce allocation churn when panels appear/disappear frequently.
See: [Render Target Strategies](/docs/game-runtimes/unity/components#render-target-strategies).
## One Rive file vs multiple files
For multi-screen UI, this usually means choosing between:
* **One larger `.riv` file** that contains multiple screens (often as components), with transitions and logic handled inside Rive.
* **Multiple `.riv` files** (for example, one per menu) that you enable or instantiate and swap via C#.
The best structure depends on both performance goals and how you want to author and maintain your files.
#### One larger file
* **Pros**: Keep a full UI flow in one place, drive transitions inside Rive, and test the entire experience in the Rive Editor. The file could be loaded once (e.g. at the start of the game) and can be reused across multiple screens or menus.
* **Tradeoff**: The entire file (including its embedded assets) stays in memory even if you're only using part of it or displaying a single artboard.
#### Multiple files
For example, one per screen or menu:
* **Pros**: Load/unload screen-specific UI to save memory. This works well when different screens have different asset needs.
* **Tradeoffs**:
* Transitions between screens typically happen in C# (outside Rive).
* Be sure to **disable/destroy** widgets you aren't using so they aren't advancing or rendering.
#### Hybrid approach: data binding with artboard slots
If you want modular loading *and* want to stay in a single widget/panel, a hybrid approach can work well:
* Keep a “main” UI file that owns the overall layout and interaction flow.
* Use data binding to load other artboards (stored in other `.riv` files) into slots at runtime.
This lets you keep UI modular while still rendering through a shared panel and widget.
The main tradeoff here is that you may need to create more files and manage them more carefully, and you may need to create more data binding properties and logic to manage the different artboards.
See:
* Unity: [Data Binding](/docs/game-runtimes/unity/data-binding)
* Concepts: [Data binding artboards](/docs/game-runtimes/unity/data-binding#artboards)
## Embedded vs referenced assets
Most projects can start with embedded assets and only optimize when needed. Referenced assets become useful when you need better reuse and control.
* **Embedded assets**: Simple workflow; everything is inside the `.riv` file.
* **Referenced assets**: Useful when you reuse the same fonts/images/audio across multiple `.riv` files, or when you want to swap assets at runtime without duplicating them across files.
Referenced assets can also help you:
* **Avoid duplicating assets** when the same images/fonts are used across multiple Rive files.
* **Swap to lower-resolution assets** on memory-constrained devices (keeping the same aspect ratio). If your Rive layout is set up to adapt, the graphic can adjust cleanly.
See: [Loading Assets (Unity)](/docs/game-runtimes/unity/loading-assets).
# Components
Source: https://rive.app/docs/game-runtimes/unity/components
The Rive Unity package includes components to help you integrate Rive into your project quickly and easily. These components are a high-level abstraction over the [low-level API](/docs/game-runtimes/unity/fundamentals), handling rendering and pointer input, and providing a unified way of working with Rive across render pipelines.
We recommend working with these components unless you have specific rendering needs or need more control over where Rive fits into your graphics pipeline, in which case you can use the low-level API.
## Rive Panel
The Rive Panel is the foundation for displaying Rive graphics in Unity. It acts as a viewport that manages and renders a collection of Rive widgets to a render texture, with the panel's dimensions determining the render target's size.
The panel renders multiple widgets to a single render texture, which means you draw different Rive files and artboards to one texture by placing multiple Rive Widgets under the same panel. This is more performant than using multiple panels as each panel renders to a separate texture by default.
#### Setup
Create an instance: Right-click in the scene hierarchy `→ Rive → Rive Panel`
A Rive Panel simply handles drawing the graphics to a texture. You'll need to use the Rive Panel **with** a [Panel Renderer](/docs/game-runtimes/unity/components#panel-renderers) to display the texture.
**Configuration**
| Property | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom Render Target Strategy | The strategy to use for rendering the panel. By default, each panel renders to a single RenderTexture that matches the panel's dimensions. You can provide a different strategy to render to a pool of textures or share a texture between panels. |
| Update Mode | Controls how the panel updates widgets: `Auto` for automatic updates each frame, or `Manual` for explicit control through `Tick()`. |
| Disable Editor Preview | Use this to prevent the panel from rendering in Edit mode in the Unity Editor |
| Multitouch Support | When `Disabled`, the panel collapses all input to a single pointer for legacy behavior. When `Enabled`, multiple pointers are tracked independently. |
**Properties**
| Property | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| Widget Container | The RectTransform that holds the panel's widgets. |
| Widgets | A read-only list of widgets managed by the panel. |
| Render Texture | The current render texture where widgets are drawn. |
| Scale In RenderTexture | The scale of the panel within its render texture. Returns `Vector2.one` if no render strategy is set. |
| Offset In Render Texture | The offset of the panel within its render texture. Returns `Vector2.one` if no render target strategy is set. |
| Is Rendering | Whether the panel is currently registered with a render target strategy and actively rendering. |
**Public Methods**
| Name | Description |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Tick(float deltaTime) | Updates all widgets in the panel. Called automatically in Auto mode. You only need to call this if the `Update Mode` is set to `Manual` |
| StartRendering() | Begins rendering the panel if it isn't already rendering to a render texture. |
| StopRendering() | Stops rendering the panel if it's currently rendering to a render texture |
| SetDimensions(Vector2 dimensions) | Sets the width and height of the panel. |
| RegisterInputProvider(IPanelInputProvider provider) | Registers a custom input provider for handling pointer events. |
| UnregisterInputProvider(IPanelInputProvider provider) | Removes a previously registered input provider. |
## Rive Widget
The Rive Widget is the primary component for displaying Rive artboards in Unity. It handles loading Rive files either from assets during edit time or from runtime-loaded files (useful when loading Rive content from a server or Addressables). The component manages artboard and state machine setup, automatically configuring everything needed to display your graphic.
#### Setup
Create an instance: Right-click in the scene hierarchy `→ Rive → Widgets → RiveWidget`
Rive Widgets must be placed under a RivePanel to be displayed.
**Configuration**
| Field | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Asset | The Rive asset (.riv) to load. |
| Artboard Name | The name of the artboard to load from the Rive file. |
| State Machine Name | The name of the state machine to load from the selected artboard. |
| Hit Test Behaviour | How pointer events are handled (Opaque, Translucent, Transparent, None). |
| Fit | How the artboard should fit within the widget's bounds. See the [Fit docs](/docs/game-runtimes/unity/layouts#the-fit-mode) for more details about the available options. |
| Alignment | How to align the artboard within the widget's bounds |
| Layout Scale Factor | Scale multiplier used when in Layout fit mode. |
| Layout Scaling Mode | How the widget scales in Layout mode ( `ReferenceArtboardSize` , `ConstantPhysicalSize` and `ConstantPixelSize`). |
| Fallback DPI | DPI value to use when screen DPI is unavailable |
| Reference DPI | Target DPI for scaling calculations |
| Speed | Controls the widget's playback speed. A value of 1 is normal speed, 2 is double speed, 0.5 is half speed |
**Properties**
| Name | Description |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| File | The currently loaded Rive file instance. |
| Artboard | The currently loaded artboard instance. |
| State Machine | The currently loaded state machine instance. |
| Status | Current status of the widget (Uninitialized, Loading, Loaded, Error) |
| BindingMode | Determines how the widget should handle binding to a ViewModel instance: Auto Bind Default, Auto Bind Selected, or Manual. |
| ViewModelInstanceName | The name of the ViewModel instance to bind to when BindingMode is set to Auto Bind Selected. |
**Events**
| Name | Description |
| --------------------- | ------------------------------------------------------------------------------------ |
| OnRiveEventReported | Triggered when a Rive event is reported from the Rive graphic. |
| OnWidgetStatusChanged | Triggered when the widget's status changes (e.g., from Loading to Loaded, or Error). |
**Public Methods**
| Name | Description |
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Load(File file) | Loads a Rive file using the default artboard and state machine. |
| Load(File file, string artboardName, string stateMachineName) | Loads a Rive file with specified artboard and state machine. |
| Load(Asset asset) | Loads from a Rive asset using the default artboard and state machine. The Rive Widget manages the lifecycle of the loaded file and cleans up the file when the widget is destroyed or when a new asset is passed in. |
| Load(Asset asset, string artboardName, string stateMachineName) | Loads from a Rive asset with specified artboard and state machine. The Rive Widget manages the lifecycle of the loaded file and cleans up the file when the widget is destroyed or when a new asset is passed in. |
If you use any Load methods that accept a `File` instance, you're responsible for disposing of the File instance when you no longer need it. This is not the case when loading from an `Asset` as the Rive Widget manages the lifecycle of the underlying file (loading and disposing).
## Procedural Rive Widget
The Procedural Rive Widget enables runtime generation of graphics using the Rive's Renderer. This component lets you create graphics programmatically using Rive's drawing primitives (paths and paints, etc.).
See [this example](https://github.com/rive-app/rive-unity/blob/0765c81e1b68e77fcbf0e62afee7290eff400d17/tests/package/PlayModeTests/Components/Goldens/TestPanels/TestProceduralDrawing.cs) for a practical implementation of a procedural drawing.
#### Setup
Create an instance: Right-click in the scene hierarchy `→ Rive → Widgets → Procedural Rive Widget`
Procedural Rive Widgets must be placed under a RivePanel to be displayed.
**Configuration**
| Property | Description |
| ------------------ | ------------------------------------------------------------------------ |
| Procedural Drawing | ProceduralDrawing instance that defines what to draw. |
| Hit Test Behavior | How pointer events are handled (Opaque, Translucent, Transparent, None). |
**Properties**
| Name | Description |
| ------ | --------------------------------------------------------------------- |
| Status | Current status of the widget (Uninitialized, Loading, Loaded, Error). |
**Events**
| Name | Description |
| --------------------- | ------------------------------------------------------------------------------------ |
| OnWidgetStatusChanged | Triggered when the widget's status changes (e.g., from Loading to Loaded, or Error). |
**Public Methods**
| Name | Description |
| ----------------------------------------- | ----------------------------------------------- |
| Load(ProceduralDrawing proceduralDrawing) | Loads a new procedural drawing into the widget. |
## Panel Renderers
Panel renderers connect RivePanel's render texture to Unity's display systems. By separating rendering logic from display concerns, we can support different rendering contexts (UI, world space, etc.) while keeping the core Rive functionality consistent.
Each renderer type specializes in a specific Unity rendering pathway, handling details like input systems and render order automatically.
### Rive Canvas Renderer
The Rive Canvas Renderer displays Rive content within Unity's UI system (uGUI). It automatically configures the necessary Canvas components and handles UI-specific concerns like proper render order and raycasting.
The Rive Canvas Renderer controls the size of the Rive Panel based on the uGUI Canvas it is being displayed under.
You can also create a Rive Panel that has been automatically configured to use this component by right-clicking in the scene hierarchy and navigating to the `Rive > Rive Panel (Canvas)`
#### Requirements
* Requires an EventSystem in the scene for pointer input
* Requires a GraphicRaycaster on the Canvas for pointer input
* The Rive Panel must be under a uGUI canvas
* This component must be placed on the same game object as the Rive Panel
#### Configuration
| Property | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Pointer Input Mode | Controls whether the renderer accepts pointer input (Enable/Disable). |
| Custom Material | A custom UI material to use when rendering the Rive graphic. |
| Match Canvas Resolution | Whether to match the canvas resolution to the RivePanel's resolution. This is useful for keeping the Rive graphic crisp when using a `Canvas Scaler`. By default, the RivePanel resolution is defined by its rect transform's width and height. Setting this to `true` will result in the RivePanel being rendered at a higher resolution than the panel's rect transform's size if needed. This feature is currently only supported when the RivePanel uses the `SimpleRenderTargetStrategy`, which is the default strategy used if none is provided. |
#### Properties
| Name | Description |
| ---------- | ------------------------- |
| Rive Panel | The panel being rendered. |
### Rive Texture Renderer
Rive Texture Renderer projects Rive content onto materials in your 3D scene. It bridges the gap between Rive's 2D rendering system and Unity's 3D material system, making it easy to apply Rive content to any mesh in your scene.
#### Requirements
* Must be placed on a GameObject with a MeshRenderer on it. e.g. a Cube, Plane, Quad, or Capsule.
* Requires an EventSystem in the scene for pointer input
* Requires a PhysicsRaycaster on the camera for pointer input in 3D space
* Target GameObject needs a MeshCollider for pointer input
#### Configuration
| Field | Description |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Renderer | The Unity Renderer component (e.g., MeshRenderer,) that will display the Rive content. |
| Material Texture Assignment Mode | Controls which texture properties are set to the Rive Panel's render texture: MainTexture sets the `_MainTex` property on the material. The `TextureProperties` option lets you select multiple specific texture properties. |
| Visibility Optimization | Determines if the RivePanel should stop rendering when the mesh is not visible to the camera. |
| Pointer Input Mode | Controls whether the renderer accepts pointer input (Enable/Disable). |
#### Properties
| Name | Description |
| ---------- | -------------------------------------- |
| Rive Panel | The panel being rendered to materials. |
**Public Methods**
| Name | Description |
| -------------------------- | --------------------------------------------------- |
| SetPanel(IRivePanel panel) | Assigns a new panel to render. |
| RefreshMaterials() | Updates material references after material changes. |
## Render Target Strategies
Render Target Strategies control how **Rive Panels** render to textures. They determine whether panels get individual textures or share a texture atlas, and handle details like texture creation, panel arrangement, and memory management.
### Simple Render Target Strategy
The `Simple Render Target Strategy` is the default rendering approach for **Rive Panels**, creating a dedicated render texture for each panel. It is automatically added to a Rive Panel if no custom strategy is provided.
This one-to-one mapping between panels and textures provides straightforward memory management.
The component must be attached to the same GameObject as its RivePanel
**Configuration**
| Field | Description |
| ----------- | ----------------------------------------------------------------------------------- |
| Panel | The RivePanel this strategy manages (automatically set when on same GameObject). |
| Draw Timing | When rendering occurs: `Batched` (once per frame) or `Immediate` (instant updates). |
**Properties**
| Name | Description |
| ---------- | ------------------------- |
| DrawTiming | Current draw timing mode. |
### Atlas Render Target Strategy
The Atlas Render Target Strategy enables multiple panels to share a single texture atlas, optimizing memory usage and draw calls.
By default, it uses a simple shelf-packing algorithm to efficiently arrange panels within the atlas, automatically growing the texture as needed while respecting maximum size constraints.
#### Setup
1. Create an instance: Right-click in the scene hierarchy `→ Rive → Render Target Strategies → Atlas Render Target Strategy`
* Assign to panels: Drag the strategy into the `Custom Render Target Strategy` field on desired panels
* (Optional) Configure atlas parameters like starting size and resolution limits
#### Notes
* Panels sharing the same strategy instance share the same atlas texture
* Create multiple strategy instances to group panels into different atlases
**Configuration**
| Field | Description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Starting Size | Initial dimensions of the atlas texture (e.g., 1024x1024). |
| Max Atlas Size | Maximum dimensions the atlas can grow to (e.g., 2048x2048). |
| Max Resolution Per Panel | Maximum resolution for any single panel. Larger panels are scaled down (e.g., an 800x400 panel with max value of 512 becomes 512x256). |
| Padding | Space between panels in the atlas to prevent texture bleeding. |
| Draw Timing | When rendering occurs: `Batched` (once per frame) or `Immediate` (instant updates). |
| Custom Atlas Packing Provider | Optional custom packing algorithm (defaults to shelf packing if not specified). |
**Properties**
| Name | Description |
| ---------------- | -------------------------------------------------------- |
| Packing Strategy | Current strategy used for arranging panels in the atlas. |
| Draw Timing | Current draw timing mode. |
**Public Methods**
| Name | Description |
| --------------------------------------------------------------------------------------- | ----------------------------------------------- |
| Configure(Vector2Int startingSize, Vector2Int maxSize, int maxResPerPanel, int padding) | Sets up atlas parameters before initialization. |
### Pooled Render Target Strategy
The **Pooled Render Target Strategy** optimizes memory usage by maintaining a pool of reusable render textures. Instead of creating a new texture for each panel or sharing a single atlas, this strategy draws panels to textures from a managed pool, recycling them as needed.
This approach is particularly useful for scenes with dynamic UI elements that appear and disappear frequently. For example, in a game with popup menus or tooltips that show Rive graphics, the strategy can reuse textures as UI elements are shown and hidden, avoiding constant texture allocation and deallocation.
#### Configuration
| Field | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pooled Texture Size | Size of textures in the pool (all pooled textures share these dimensions). If a given Rive Panel doesn't match the texture's aspect ratio, the panel will be resized to fit within the texture while maintaining its aspect ratio. |
| Initial Pool Size | The initial allocated size of the pool. |
| Max Pool Size | Maximum number of textures the pool can contain. |
| Pool Overflow Behavior | How to handle requests when pool is full: `Flexible` (create temporary textures) or `Fixed` (reject new panels). |
| Draw Timing | When rendering occurs: `Batched` (once per frame) or `Immediate` (instant updates). |
#### Properties
| Name | Description |
| ------------------- | -------------------------------------------- |
| Pool Overflow | Current overflow behavior setting. |
| Pooled Texture Size | Current texture dimensions used by the pool. |
| Initial Pool Size | Current initial pool capacity. |
| Max Pool Size | Current maximum pool size. |
| Draw Timing | Current draw timing mode. |
#### Public Methods
| Name | Description |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Configure(Vector2Int textureSize, int initialSize, int maxSize, PoolOverflowBehavior behavior) | Sets up pool parameters (must be called before the first Rive Panel is registered). |
# Data Binding
Source: https://rive.app/docs/game-runtimes/unity/data-binding
Connect your code to bound editor elements using View Models
Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the [Overview](/docs/editor/data-binding/overview).
# 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](#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.
These APIs are only needed when the `Data Binding Mode` on the RiveWidget is set to `Manual`.
Otherwise, you can configure view model binding directly in the Unity Inspector under the Data section.
```csharp theme={null}
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
File file = riveWidget.File;
// Get reference by name
ViewModel viewModel = file.GetViewModelByName("My View Model");
// Get reference by index
for (int i = 0; i < file.ViewModelCount; i++)
{
ViewModel indexedVM = file.GetViewModelAtIndex(i);
}
// Get reference to the default view model for an artboard
ViewModel defaultVM = riveWidget.Artboard.DefaultViewModel;
}
}
```
# 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:
| Type | Value |
| ----------------- | --------------- |
| Number | 0 |
| String | Empty string |
| Boolean | False |
| Color | 0xFF000000 |
| Trigger | Untriggered |
| Enum | The first value |
| Image | No image |
| Font | No font |
| Artboard | No artboard |
| List | Empty list |
| Nested view model | Null |
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.
In some samples, due to the wordiness of "view model instance", we use the abbreviation "VMI", as well as "VM" for "view model".
These APIs are only needed when the `Data Binding Mode` on the RiveWidget is set to `Manual`.
Otherwise, you can configure view model binding directly in the Unity Inspector under the Data section.
```csharp theme={null}
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
// From a ViewModel reference
ViewModel vm = riveWidget.File.GetViewModelByName("My View Model");
// Create blank
ViewModelInstance vmiBlank = vm.CreateInstance();
// Create default
ViewModelInstance vmiDefault = vm.CreateDefaultInstance();
// Create by index
for (int i = 0; i < vm.InstanceCount; i++)
{
ViewModelInstance vmiIndexed = vm.CreateInstanceAt(i);
}
// Create by name
ViewModelInstance vmiNamed = vm.CreateInstanceByName("My Instance");
}
}
```
### Binding
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.
```csharp theme={null}
// Access the RiveWidget component
// Using the Unity Inspector
// 1. Select your RiveWidget in the Inspector
// 2. In the "Data" section, set the Data Binding Mode:
// - Auto Bind Default: Automatically binds the default view model instance
// - Auto Bind Selected: Uses a specific instance you select in the dropdown
// - Manual: Requires you to manually set up binding in code
// Or programmatically if set to Manual or if using the low-level API
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModel vm = riveWidget.Artboard.DefaultViewModel;
ViewModelInstance vmi = vm.CreateDefaultInstance();
// Applying to a state machine will automatically bind to its artboard
riveWidget.StateMachine.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.
*Rive Widget*\* provides both visual and programmatic ways to configure auto-binding. In the Inspector, you can easily set up binding through the Data Binding Mode dropdown:
To enable auto-binding programmatically, use the following APIs:
```csharp theme={null}
// Before the widget is loaded:
// Option 1: Auto bind the default instance
riveWidget.BindingMode = DataBindingMode.AutoBindDefault;
// Option 2: Auto bind a specific instance by name
riveWidget.BindingMode = DataBindingMode.AutoBindSelected;
riveWidget.ViewModelInstanceName = "My Instance";
// Load the Rive file after setting the binding mode
riveWidget.Load(riveFile, artboardName, stateMachineName);
...
// Access the current instance that was auto-bound
ViewModelInstance boundInstance = riveWidget.StateMachine.ViewModelInstance;
```
If you choose to use the low-level API to control the render loop, you'll need to manually set up data binding in your scripts.
For reference, check this [RiveScreen example](https://github.com/rive-app/rive-unity/blob/main/examples/basic/Assets/GameRuntime/RiveScreen.cs) that demonstrates one way to implement auto binding in a custom render loop.
Using the low-level API requires additional implementation effort and understanding of the Rive runtime. Unless you have a specific need to setup a custom render loop, we recommend using the Component API.
# 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:
| Type | Supported |
| ---------------------- | --------- |
| Floating point numbers | ✅ |
| Booleans | ✅ |
| Triggers | ✅ |
| Strings | ✅ |
| Enumerations | ✅ |
| Colors | ✅ |
| Nested View Models | ✅ |
| Lists | ✅ |
| Images | ✅ |
| Artboards | ✅ |
For more information on version compatibility, see the [Feature Support](/docs/feature-support) page.
### Listing Properties
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.
```csharp theme={null}
var vm = riveWidget.File.GetViewModelByName("My View Model");
// A list of properties
var properties = vm.Properties;
foreach (var prop in properties)
{
Debug.Log($"Property: {prop.Name}, Type: {prop.Type}");
}
```
### Reading and Writing 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.
```csharp theme={null}
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
// Check if the widget is loaded before accessing the view model instance
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
//==========================================================================
// STRING PROPERTIES
//==========================================================================
ViewModelInstanceStringProperty stringProperty = viewModelInstance.GetStringProperty("title");
Debug.Log($"String value: {stringProperty.Value}");
stringProperty.Value = "New Text";
//==========================================================================
// NUMBER PROPERTIES
//==========================================================================
ViewModelInstanceNumberProperty numberProperty = viewModelInstance.GetNumberProperty("count");
Debug.Log($"Number value: {numberProperty.Value}");
numberProperty.Value = 42.5f;
//==========================================================================
// BOOLEAN PROPERTIES
//==========================================================================
ViewModelInstanceBooleanProperty boolProperty = viewModelInstance.GetBooleanProperty("isActive");
Debug.Log($"Boolean value: {boolProperty.Value}");
boolProperty.Value = true;
//==========================================================================
// COLOR PROPERTIES
//==========================================================================
ViewModelInstanceColorProperty colorProperty = viewModelInstance.GetColorProperty("backgroundColor");
// Using Unity Color (float values 0-1)
Color currentColor = colorProperty.Value;
colorProperty.Value = new UnityEngine.Color(1, 0, 0, 1); // Red color
// Or using Color32 (byte values 0-255)
Color32 currentColor32 = colorProperty.Value32;
colorProperty.Value32 = new Color32(0, 255, 0, 255); // Green color
//==========================================================================
// ENUM PROPERTIES
//==========================================================================
ViewModelInstanceEnumProperty enumProperty = viewModelInstance.GetEnumProperty("category");
Debug.Log($"Enum current value: {enumProperty.Value}");
Debug.Log($"Enum available values: {string.Join(", ", enumProperty.EnumValues)}");
enumProperty.Value = "option_name";
//==========================================================================
// TRIGGER PROPERTIES
//==========================================================================
ViewModelInstanceTriggerProperty triggerProperty = viewModelInstance.GetTriggerProperty("onSubmit");
triggerProperty.Trigger(); // Fire the trigger
}
}
```
### 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.
```csharp theme={null}
if (riveWidget.Status == WidgetStatus.Loaded)
{
var viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Accessing nested view models using chaining
var nestedNumberByChain = viewModelInstance
.GetViewModelInstanceProperty("My Nested View Model")
.GetViewModelInstanceProperty("My Second Nested VM")
.GetNumberProperty("My Nested Number");
// Accessing nested properties using path notation
var nestedNumberByPath = viewModelInstance
.GetNumberProperty("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.
```csharp theme={null}
private ViewModelInstanceNumberProperty numberProperty;
private ViewModelInstanceStringProperty stringProperty;
private ViewModelInstanceBooleanProperty boolProperty;
private ViewModelInstanceColorProperty colorProperty;
private ViewModelInstanceEnumProperty enumProperty;
private ViewModelInstanceTriggerProperty triggerProperty;
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Add listeners to properties
numberProperty = viewModelInstance.GetNumberProperty("count");
numberProperty.OnValueChanged += OnNumberPropertyChanged;
stringProperty = viewModelInstance.GetStringProperty("title");
stringProperty.OnValueChanged += OnStringPropertyChanged;
boolProperty = viewModelInstance.GetBooleanProperty("isActive");
boolProperty.OnValueChanged += OnBoolPropertyChanged;
colorProperty = viewModelInstance.GetColorProperty("backgroundColor");
colorProperty.OnValueChanged += OnColorPropertyChanged;
enumProperty = viewModelInstance.GetEnumProperty("category");
enumProperty.OnValueChanged += OnEnumPropertyChanged;
triggerProperty = viewModelInstance.GetTriggerProperty("onSubmit");
triggerProperty.OnTriggered += OnTriggerPropertyFired;
}
}
private void OnNumberPropertyChanged(float newValue)
{
Debug.Log($"Number changed to: {newValue}");
}
private void OnStringPropertyChanged(string newValue)
{
Debug.Log($"String changed to: {newValue}");
}
private void OnBoolPropertyChanged(bool newValue)
{
Debug.Log($"Boolean changed to: {newValue}");
}
private void OnColorPropertyChanged(UnityEngine.Color newValue)
{
Debug.Log($"Color changed to: {ColorUtility.ToHtmlStringRGBA(newValue)}");
}
private void OnEnumPropertyChanged(string newValue)
{
Debug.Log($"Enum changed to: {newValue}");
}
private void OnTriggerPropertyFired()
{
Debug.Log("Trigger fired!");
}
private void OnDestroy()
{
// You should remove listeners when no longer needed,
numberProperty.OnValueChanged -= OnNumberPropertyChanged;
stringProperty.OnValueChanged -= OnStringPropertyChanged;
boolProperty.OnValueChanged -= OnBoolPropertyChanged;
colorProperty.OnValueChanged -= OnColorPropertyChanged;
enumProperty.OnValueChanged -= OnEnumPropertyChanged;
triggerProperty.OnTriggered -= OnTriggerPropertyFired;
}
```
### Images
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.
```csharp theme={null}
[SerializeField] private ImageOutOfBandAsset m_lightImageAsset;
[SerializeField] private ImageOutOfBandAsset m_darkImageAsset;
private ViewModelInstanceImageProperty imageProperty;
private bool isDarkMode = false;
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
m_lightImageAsset.Load();
m_darkImageAsset.Load();
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Get the image property by name
imageProperty = viewModelInstance.GetImageProperty("profileImage");
// or alternatively:
// imageProperty = viewModelInstance.GetProperty("profileImage");
// Set up change callback
imageProperty.OnValueChanged += OnImageChanged;
// Set initial image (light mode)
imageProperty.Value = m_lightImageAsset;
}
}
private void OnImageChanged()
{
Debug.Log("Image updated!");
}
// Example method to toggle between light and dark mode images
public void ToggleTheme()
{
if (imageProperty != null)
{
isDarkMode = !isDarkMode;
imageProperty.Value = isDarkMode ? m_darkImageAsset : m_lightImageAsset;
}
}
// Example method to clear the image
public void ClearImage()
{
if (imageProperty != null)
{
imageProperty.Value = null;
}
}
private void OnDestroy()
{
m_lightImageAsset.Unload();
m_darkImageAsset.Unload();
// Remove the event listener
if (imageProperty != null)
{
imageProperty.OnValueChanged -= OnImageChanged;
}
}
```
For a demo of image data binding in Unity, see the **Image Data Binding** scene in the [Rive Unity Examples repository](https://github.com/rive-app/rive-unity-examples).
### Render Textures
**Experimental Feature**
This feature is not enabled out of the box. Add `RIVE_USING_EXPERIMENTAL` under **Project Settings → Player → Scripting Define Symbols** to use it.
These APIs are still in development and may change before they become stable.
Unity applies scripting defines per platform. If you ship to multiple targets (Standalone, iOS, Android, and so on), add the define on each platform tab where you need this feature.
The examples above use `ImageOutOfBandAsset` to bind static raster images loaded from disk. Use `RenderTextureImageSource` when the image comes from a live Unity `RenderTexture` instead, such as `VideoPlayer` output, a camera render target, or custom GPU content you change each frame.
`RenderTextureImageSource` wraps the texture as a native Rive image and binds it to a view model image property through `SetFromRenderTextureImageSource`. Once bound, the runtime keeps the visuals up to date for you.
The example below binds a `VideoPlayer` render target to a view model image property named `"video"`. Wait until the Rive widget is loaded before binding, then call `SetFromRenderTextureImageSource` once. The runtime handles per-frame updates after that.
```csharp theme={null}
#if RIVE_USING_EXPERIMENTAL
using System.Collections;
using UnityEngine;
using UnityEngine.Video;
using Rive;
using Rive.Components;
public class VideoImageBinding : MonoBehaviour
{
[SerializeField] private RiveWidget riveWidget;
[SerializeField] private RenderTexture videoTexture;
[SerializeField] private string viewModelImagePath = "video";
[SerializeField] private VideoPlayer videoPlayer;
[Tooltip("How the video is adapted before Rive samples it.")]
[SerializeField]
private RenderTextureImageSource.TextureProcessingMode processingMode =
RenderTextureImageSource.TextureProcessingMode.Auto;
private RenderTextureImageSource renderTextureSource;
private void Start()
{
if (videoTexture == null || riveWidget == null || videoPlayer == null)
{
Debug.LogWarning("Assign videoTexture, riveWidget, and videoPlayer in the Inspector.");
return;
}
videoPlayer.renderMode = VideoRenderMode.RenderTexture;
videoPlayer.targetTexture = videoTexture;
videoPlayer.prepareCompleted += OnVideoPrepared;
videoPlayer.Prepare();
}
private void OnVideoPrepared(VideoPlayer source)
{
StartCoroutine(BindAndPlay());
}
private IEnumerator BindAndPlay()
{
while (riveWidget.Status != WidgetStatus.Loaded ||
riveWidget.StateMachine == null)
{
yield return null;
}
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
if (viewModelInstance == null)
{
Debug.LogWarning("No ViewModelInstance on the widget.");
yield break;
}
ViewModelInstanceImageProperty imageProperty =
viewModelInstance.GetImageProperty(viewModelImagePath);
if (imageProperty == null)
{
Debug.LogWarning($"Image property '{viewModelImagePath}' not found.");
yield break;
}
renderTextureSource = new RenderTextureImageSource(videoTexture, processingMode);
imageProperty.SetFromRenderTextureImageSource(renderTextureSource);
videoPlayer.Play();
}
private void OnDestroy()
{
if (videoPlayer != null)
{
videoPlayer.prepareCompleted -= OnVideoPrepared;
videoPlayer.Stop();
videoPlayer.targetTexture = null;
}
renderTextureSource?.Dispose();
}
}
#endif
```
#### When to use it
| Approach | Use when |
| -------------------------- | ------------------------------------------------------------------------ |
| `ImageOutOfBandAsset` | You have a static image file (PNG, JPG, WebP, etc.) loaded at runtime |
| `RenderTextureImageSource` | You already have a Unity `RenderTexture` whose contents change over time |
Both APIs bind to the same view model image property type. A property can only be driven by one source at a time. Assigning an `ImageOutOfBandAsset` through `Value` automatically unbinds an active render-texture source, and vice versa.
#### Requirements and limitations
The source must be a stable, user-allocated 2D `RenderTexture`. Do not use transient RenderGraph resources. Their backing memory can be reused and produce stale samples or crashes.
Supported source formats:
* Single-sample, non-MSAA
* 2D only (not cube, array, or 3D)
* Created and kept alive for as long as the binding is active
Supported graphics backends:
| Backend | Supported |
| -------------- | --------- |
| Metal | Yes |
| Direct3D 11 | Yes |
| Direct3D 12 | Yes |
| Vulkan | Yes |
| OpenGL / WebGL | No |
On unsupported backends, binding safe-fails: the property stays empty and an error is logged.
Rive composites through an 8-bit internal render target, so HDR source values above 1.0 are clamped at the Rive layer.
#### Texture processing
Unity backends differ in whether render texture texels are stored top-down or bottom-up. In Linear color space projects, Unity may also hand Rive gamma-encoded values that get decoded twice when the panel composites.
To avoid requiring every project to handle these fixes manually, the default `TextureProcessingMode.Auto` blits through an owned intermediate render texture and applies a flip and/or gamma re-encode only when the active backend or project settings actually need it.
| Mode | Behavior |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `Auto` | Apply orientation and color fixes when needed. This is the default processing mode. |
| `Orientation` | Flip upside-down textures on backends that store texels top-down. Leave color alone. |
| `Color` | Re-encode to gamma in Linear projects so colors composite correctly. Leave orientation alone. |
| `None` | Bind the source render texture directly with no intermediate blit. Use this when your texture is already correctly oriented and encoded. |
```csharp theme={null}
// Default: let the runtime decide what processing is needed
var source = new RenderTextureImageSource(renderTexture);
// Opt out when you already produce a correctly oriented, correctly encoded texture
var directSource = new RenderTextureImageSource(
renderTexture,
RenderTextureImageSource.TextureProcessingMode.None);
```
#### Refresh behavior
`RenderTextureImageSource` controls how often the bound image property is updated from the source texture.
| Mode | Behavior |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| `PerFrame` | Rebuild and re-push every frame. This is the default refresh mode. Use for live sources such as video or camera output. |
| `Manual` | Update only when you call `Refresh()`. Use for snapshots, baked textures, or content that isn't updated very often. |
```csharp theme={null}
// Live source (default)
var liveSource = new RenderTextureImageSource(
renderTexture,
refreshMode: RenderTextureImageSource.RefreshMode.PerFrame);
// Snapshot / on-demand source
var snapshotSource = new RenderTextureImageSource(
renderTexture,
refreshMode: RenderTextureImageSource.RefreshMode.Manual);
// After writing new content into the render texture:
snapshotSource.Refresh();
```
#### Clearing and switching images
To clear a render-texture-backed image:
```csharp theme={null}
imageProperty.SetFromRenderTextureImageSource(null);
```
To switch back to a regular image asset:
```csharp theme={null}
imageAsset.Load();
imageProperty.Value = imageAsset;
```
Assigning `Value` automatically detaches any active `RenderTextureImageSource` binding on that property.
#### Lifecycle and teardown
While a `RenderTextureImageSource` is bound to at least one image property, the runtime keeps it alive and updating.
When you are done with a source, call `Dispose()` to stop updates and release any intermediate GPU resources owned by Rive.
##### Recommended teardown order
`RenderTextureImageSource` does not own your `RenderTexture`. When shutting down, use this order:
1. **Stop producers.** Stop or disconnect anything still writing into the texture (`VideoPlayer`, camera, custom blit loop, etc.).
2. **Unbind and dispose the image source.** Call `Dispose()` on the `RenderTextureImageSource`, or call `SetFromRenderTextureImageSource(null)` and then `Dispose()`.
3. **Release your render texture (if you created it).** If you allocated the `RenderTexture` at runtime, call `Release()` and then `Destroy()` on it.
```csharp theme={null}
private void OnDestroy()
{
// 1. Stop writing into the texture
if (videoPlayer != null)
{
videoPlayer.Stop();
videoPlayer.targetTexture = null;
}
// 2. Unbind and dispose the Rive image source
renderTextureSource?.Dispose();
renderTextureSource = null;
// 3. Only if you created the RenderTexture at runtime
if (ownsRenderTexture && renderTexture != null)
{
renderTexture.Release();
Destroy(renderTexture);
}
}
```
If your `RenderTexture` is a project asset assigned in the Inspector (as in the video example above), you only need steps 1 and 2. Do not call `Destroy()` on shared or asset-backed render textures.
Destroying or releasing the source texture before step 2 is tolerated (the bound property clears on the next tick), but disposing the image source first is the safer path. It avoids Rive attempting to wrap a texture that is already being released.
### Lists
List properties let you manage a dynamic set of view model instances at runtime. For example, you can build a to-do app where users can add and remove tasks in a scrollable Layout.
See the [Editor section](/docs/editor/data-binding/lists) on creating data bound lists.
A single list property can include different view model types, with each view model tied to its own Component, making it easy to populate a list with a variety of Component instances.
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](/docs/editor/data-binding/lists#view-model-list-property) editor documentation.
```csharp theme={null}
private ViewModelInstanceListProperty listProperty;
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Get the list property by name
listProperty = viewModelInstance.GetListProperty("todos");
// or alternatively:
// var listProperty = viewModelInstance.GetProperty("todos");
Debug.Log($"List count: {listProperty.Count}");
// Set up change callback
listProperty.OnChanged += OnListChanged;
// Get the view model for creating new instances
var todoItemVM = riveWidget.File.GetViewModelByName("TodoItem");
// Create a blank instance from the view model
var newTodo = todoItemVM.CreateInstance();
newTodo.GetStringProperty("description").Value = "Buy groceries";
// Add the newly created instance to the list
listProperty.Add(newTodo);
// Insert an instance at a specific index
var anotherTodo = todoItemVM.CreateInstance();
listProperty.Insert(anotherTodo, 0); // Insert at beginning
// Access items by index
for (int i = 0; i < listProperty.Count; i++)
{
var item = listProperty.GetInstanceAt(i);
Debug.Log($"Item {i}: {item}");
}
// Remove a specific instance from the list
listProperty.Remove(newTodo);
// Remove instance at index
listProperty.RemoveAt(0);
// Swap two instances in the list at index 0 and 1
if (listProperty.Count > 1)
{
listProperty.Swap(0, 1);
}
}
}
private void OnListChanged()
{
Debug.Log("List updated!");
}
private void OnDestroy()
{
if (listProperty != null)
{
listProperty.OnChanged -= OnListChanged;
}
}
```
### Artboards
Artboard properties allows you to swap out entire components at runtime. This is useful for creating modular components that can be reused across different designs or applications, for example:
* Creating a skinning system that supports a large number of variations, such as a character creator where you can swap out different body parts, clothing, and accessories.
* Creating a complex scene that is a composition of various artboards loaded from various different Rive files (drawn to a single canvas/texture/widget).
* Reducing the size (complexity) of a single Rive file by breaking it up into smaller components that can be loaded on demand and swapped in and out as needed.
Artboard properties work with the `BindableArtboard` class, which is different from the regular `Artboard` class in the package.
`BindableArtboard` is a runtime wrapper for interacting with artboards through data binding. These instances reference existing artboards in your file, so no additional setup is required in the Rive Editor.
```csharp theme={null}
[SerializeField] private Asset m_externalRiveAsset;
private ViewModelInstanceArtboardProperty artboardProperty;
private File externalFile;
private void OnEnable()
{
riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
}
private void OnDisable()
{
riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Get the artboard property by name
artboardProperty = viewModelInstance.GetArtboardProperty("artboard_1");
// or alternatively:
// artboardProperty = viewModelInstance.GetProperty("artboard_1");
// Set up change callback
artboardProperty.OnValueChanged += OnArtboardChanged;
// Set artboard from same file.
var blueArtboard = riveWidget.File.BindableArtboard("ArtboardBlue");
artboardProperty.Value = blueArtboard;
// Load external file if needed
if (m_externalRiveAsset != null)
{
externalFile = File.Load(m_externalRiveAsset);
}
}
}
private void OnArtboardChanged()
{
Debug.Log("Artboard changed");
}
// Example method to assign a different artboard from the same file
public void SwitchToRedArtboard()
{
if (artboardProperty != null)
{
var redArtboard = riveWidget.File.BindableArtboard("ArtboardRed");
artboardProperty.Value = redArtboard;
}
}
// Example method to assign an artboard from a different file
// This is useful for creating modular components that can be reused across different Rive files.
public void SwitchToExternalArtboard()
{
if (artboardProperty != null && externalFile != null)
{
var externalArtboard = externalFile.BindableArtboard("SomeArtboard");
artboardProperty.Value = externalArtboard;
}
}
private void OnDestroy()
{
// Clean up external file
externalFile?.Dispose();
// Remove the event listener
if (artboardProperty != null)
{
artboardProperty.OnValueChanged -= OnArtboardChanged;
}
}
```
### Using Custom View Model Instances with Bindable Artboards
You can link a custom `ViewModelInstance` to a bindable artboard, giving you control over the data being used by that artboard.
To create a bindable artboard with a custom view model instance, use the overload on the `File` instance that contains the artboard:
```csharp theme={null}
var file = riveWidget.File;
var viewModelInstance = file.GetViewModelByName("CharacterData").CreateInstance();
var bindableArtboard = file.BindableArtboard("FeaturedCharacterCard", viewModelInstance);
```
**Example: Featured Content Slot**
Imagine you have a home screen with a single "featured" content area that can display different types of promotions. Each content type uses a **different artboard with its own unique data structure**. Your main UI file contains the home screen layout with a featured content slot that you can populate dynamically:
```csharp theme={null}
private ViewModelInstanceArtboardProperty featuredContentSlot;
private ViewModelInstance characterData;
private ViewModelInstance eventData;
private ViewModelInstance offerData;
private BindableArtboard featuredCharacter;
private BindableArtboard limitedEvent;
private BindableArtboard specialOffer;
private void HandleWidgetStatusChanged()
{
if (riveWidget.Status == WidgetStatus.Loaded)
{
ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Get the featured content slot from your main UI
featuredContentSlot = viewModelInstance.GetArtboardProperty("featuredContentSlot");
// Featured Character has its own unique data structure
var characterViewModel = riveWidget.File.GetViewModelByName("CharacterData");
characterData = characterViewModel.CreateInstance();
var charName = characterData.GetStringProperty("name");
var charClass = characterData.GetStringProperty("class");
var attackPower = characterData.GetNumberProperty("attackPower");
var specialAbility = characterData.GetStringProperty("specialAbility");
var isUnlocked = characterData.GetBoolProperty("unlocked");
charName.Value = "Shadowblade";
charClass.Value = "Assassin";
attackPower.Value = 92;
specialAbility.Value = "Phantom Strike";
isUnlocked.Value = false;
// Limited-Time Event has its own unique data structure
var eventViewModel = riveWidget.File.GetViewModelByName("EventData");
eventData = eventViewModel.CreateInstance();
var eventTitle = eventData.GetStringProperty("title");
var eventDescription = eventData.GetStringProperty("description");
var hoursRemaining = eventData.GetNumberProperty("hoursRemaining");
var participantCount = eventData.GetNumberProperty("participants");
var isActive = eventData.GetBoolProperty("active");
eventTitle.Value = "Dragon Raid Weekend";
eventDescription.Value = "Team up to defeat the ancient dragon";
hoursRemaining.Value = 36;
participantCount.Value = 1247;
isActive.Value = true;
// Special Offer has its own unique data structure
var offerViewModel = riveWidget.File.GetViewModelByName("OfferData");
offerData = offerViewModel.CreateInstance();
var offerName = offerData.GetStringProperty("itemName");
var originalPrice = offerData.GetNumberProperty("originalPrice");
var discountPercent = offerData.GetNumberProperty("discount");
var currency = offerData.GetStringProperty("currencyType");
var timeLeftHours = offerData.GetNumberProperty("expiresInHours");
offerName.Value = "Legendary Weapon Pack";
originalPrice.Value = 2999;
discountPercent.Value = 50;
currency.Value = "Gems";
timeLeftHours.Value = 12;
// Create bindable artboards. Each uses a DIFFERENT artboard with unique design
featuredCharacter = riveWidget.File.BindableArtboard("FeaturedCharacterCard", characterData);
limitedEvent = riveWidget.File.BindableArtboard("EventBanner", eventData);
specialOffer = riveWidget.File.BindableArtboard("OfferCard", offerData);
// Start by showing the featured character
featuredContentSlot.Value = featuredCharacter;
}
}
// Switch to showing the limited-time event
public void ShowLimitedEvent()
{
if (featuredContentSlot != null && limitedEvent != null)
{
featuredContentSlot.Value = limitedEvent;
}
}
// Switch to showing the special offer
public void ShowSpecialOffer()
{
if (featuredContentSlot != null && specialOffer != null)
{
featuredContentSlot.Value = specialOffer;
}
}
// Update event countdown timer
public void UpdateEventTimer(float deltaTime)
{
if (eventData != null)
{
var hoursRemaining = eventData.GetNumberProperty("hoursRemaining");
hoursRemaining.Value -= (deltaTime / 3600f); // Convert seconds to hours
}
}
private void OnDestroy()
{
// Clean up view model instances
characterData?.Dispose();
eventData?.Dispose();
offerData?.Dispose();
// Clean up bindable artboards
featuredCharacter?.Dispose();
limitedEvent?.Dispose();
specialOffer?.Dispose();
// Remove event listeners if any were added
if (featuredContentSlot != null)
{
featuredContentSlot.OnValueChanged -= OnArtboardChanged;
}
}
```
### 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.
```csharp theme={null}
var viewModelInstance = riveWidget.StateMachine.ViewModelInstance;
// Accessing enums from the file
var enums = riveWidget.File.ViewModelEnums;
foreach (var enumType in enums)
{
Debug.Log($"Enum: {enumType.Name}");
foreach (var value in enumType.Values)
{
Debug.Log($" - Value: {value}");
}
}
...
// Using enum properties
var enumProperty = viewModelInstance.GetEnumProperty("category");
Debug.Log($"Current value: {enumProperty.Value}");
Debug.Log($"Available values: {string.Join(", ", enumProperty.EnumValues)}");
enumProperty.Value = enumProperty.EnumValues[0]; // Set to first value
```
# FAQ
Source: https://rive.app/docs/game-runtimes/unity/faq
Common questions for the Unity runtime.
## Does Rive support Unity's Sprite Renderer?
Rive renders to a **Render Texture** in Unity. Unity's **Sprite Renderer** doesn't handle Render Textures particularly well out of the box, so using Rive directly as a sprite usually requires a workaround.
In general, we recommend building your visuals in Rive and driving behavior with **data binding**, rather than trying to treat Rive files as a drop-in replacement for individual sprites that are then assembled in Unity.
If you absolutely need a 2D-style workflow of rendering Rive files as individual objects in Unity, the recommended workaround is to render Rive onto a **Quad** (or other mesh) using the **Rive Texture Renderer**, rather than trying to display a Render Texture through a Sprite Renderer. Note that there are [performance considerations](/docs/game-runtimes/unity/best-practices) to keep in mind when using this approach.
If you choose to use a Sprite Renderer workaround (especially custom shader approaches), we can't provide support for debugging or maintaining that custom rendering path as it is not officially supported.
## Does Rive support UI Toolkit?
Not yet, but we're working on it.
Today, the recommended approach is to use **uGUI** (via the **Rive Canvas Renderer**) or render Rive to a mesh.
## Why isn't my Rive graphic displaying?
A few common gotchas:
* Your **Rive Widget** needs to be under a **Rive Panel** to be rendered.
* A camera needs to be present in the scene.
See: [Unity components](/docs/game-runtimes/unity/components).
## Unity crashes when upgrading the package. What should I do?
If Unity crashes while upgrading the package, close the Unity Editor, update the version in `Packages/manifest.json`, and then reopen the project.
See: [Getting Started](/docs/game-runtimes/unity/getting-started).
## How do I report a bug or crash?
If you hit a crash or unexpected behavior, please file an issue in the [rive-unity GitHub repo](https://github.com/rive-app/rive-unity/issues).
To help us reproduce and diagnose the issue:
* In Unity, run **Tools → Rive → Copy Support Info** and paste the output into the issue.
* Include your **Editor.log**.
See: [Bug reports](/docs/game-runtimes/unity/unity#bug-reports).
# Fundamentals
Source: https://rive.app/docs/game-runtimes/unity/fundamentals
## Adding Rive Assets
To add a `.riv` file to a Unity project, simply drag it into the Project Window. Once dropped, a **Rive Asset** will be automatically created.
Now you can display the Rive Asset within a **Rive Panel** and **Rive Widget**.
## File
A `Rive.File` contains Artboards, StateMachines, and Animations.
If you're working with the Rive Panel and Rive Widgets components, the Rive Widget will automatically handle loading the underlying Rive File from the assigned Asset.\
You only need to use this class directly if you want control of the lifecycle of the Rive File, or if you need to load the file from an external source like a CDN.
The `Rive.File` class also provides several methods to load Rive content into Unity:
If the file is already in memory, a cached version will be returned to improve performance and avoid redundant loading.
#### 1. From a Rive Asset (.riv file)
You can load a `Rive.File` from an imported `.riv` asset in the inspector:
```csharp theme={null}
public Rive.Asset asset; // pass in .riv asset in the inspector
private Rive.File m_file;
...
private void Start()
{
if (asset != null)
{
m_file = Rive.File.Load(asset);
}
}
```
#### 2. From a Unity TextAsset
You can load a `Rive.File` from a Unity `TextAsset`. This is useful if you have the bytes bundled as a `TextAsset` in the Unity project.
```csharp theme={null}
public TextAsset riveTextAsset; // assign in the inspector
private Rive.File m_file;
...
private void Start()
{
if (riveTextAsset != null)
{
m_file = Rive.File.Load(riveTextAsset);
}
}
```
#### 3. From a Byte Array
If you have the raw bytes of a `.riv` file, you can load it directly from a byte array. This method provides flexibility if you're loading the file data from a custom source or dynamically (e.g. from a CDN-stored file):
```csharp theme={null}
private byte[] riveFileBytes; // Your byte array, loaded from remote storage, for example.
private Rive.File m_file;
...
private void Start()
{
if (riveFileBytes != null)
{
m_file = Rive.File.Load(riveFileBytes, "myRiveFileName");
}
}
```
## Artboards
[Artboards](/docs/editor/fundamentals/artboards) contain [State Machines](/docs/editor/state-machine/state-machine) and Animations.
Using a **Rive Widget** component, you can select from a list of available artboards within a given **Rive File**.
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
Artboards are instantiated from a `Rive.File` instance:
```java theme={null}
private Artboard m_artboard;
...
m_artboard = m_file.Artboard(0); // by index
m_artboard = m_file.Artboard("Arboard 1"); // by name
```
## State Machines
For more information, see [State Machines](/docs/game-runtimes/unity/state-machines).
Using a **Rive Widget** component, you can select from a list of available state machines within a given Artboard.
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
State Machines are instantiated from an Artboard instance:
```csharp theme={null}
private StateMachine m_stateMachine;
...
m_stateMachine = m_artboard?.StateMachine(); // default state machine
m_stateMachine = m_artboard?.StateMachine(0); // state machine at index
m_stateMachine = m_artboard?.StateMachine("Name"); // state machine with name
```
They also control advancing (playing) an animation:
```csharp theme={null}
private void Update()
{
m_stateMachine?.Advance(Time.deltaTime);
}
```
## Rendering
In Unity, Rive renders to a [RenderTexture](https://docs.unity3d.com/ScriptReference/RenderTexture.html) that you can display in your Scene by attaching to a [Material](https://docs.unity3d.com/ScriptReference/Material.html) or anywhere else you would use a Render Texture within your project.
The **Rive Panel** automatically renders its **Rive Widgets** to a Render Texture.
Using the **Rive Canvas Renderer**, you can display a **Rive Panel** within a uGUI Canvas.
To display a **Rive Panel** on a GameObject's mesh, use the **Rive Texture Renderer.**
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
Layout and draw commands are managed through the `Rive.Renderer`.
For a more complex example drawing a texture directly to a camera, see the **getting-started** project in the [examples repository](https://github.com/rive-app/rive-unity-examples).
The following is a basic example script behaviour to render a given Rive asset to the provided `renderTexture`. The animation is played by calling `.Advance()` on the State Machine.
See [State Machines](/docs/game-runtimes/unity/state-machines) for more general information on playing state machines at runtime.
```csharp theme={null}
using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using Rive;
using LoadAction = UnityEngine.Rendering.RenderBufferLoadAction;
using StoreAction = UnityEngine.Rendering.RenderBufferStoreAction;
public class RiveTexture : MonoBehaviour
{
public Rive.Asset asset;
public RenderTexture renderTexture;
public Fit fit = Fit.contain;
public Alignment alignment = Alignment.Center;
private Rive.RenderQueue m_renderQueue;
private Rive.Renderer m_riveRenderer;
private CommandBuffer m_commandBuffer;
private Rive.File m_file;
private Artboard m_artboard;
private StateMachine m_stateMachine;
private Camera m_camera;
private void Start()
{
// If on D3d11, this is required
renderTexture.enableRandomWrite = true;
m_renderQueue = new Rive.RenderQueue(renderTexture);
m_riveRenderer = m_renderQueue.Renderer();
if (asset != null)
{
m_file = Rive.File.Load(asset);
m_artboard = m_file.Artboard(0);
m_stateMachine = m_artboard?.StateMachine();
}
if (m_artboard != null && renderTexture != null)
{
m_riveRenderer.Align(fit, alignment, m_artboard);
m_riveRenderer.Draw(m_artboard);
m_commandBuffer = m_riveRenderer.ToCommandBuffer();
m_commandBuffer.SetRenderTarget(renderTexture);
m_commandBuffer.ClearRenderTarget(true, true, UnityEngine.Color.clear, 0.0f);
m_riveRenderer.AddToCommandBuffer(m_commandBuffer);
m_camera = Camera.main;
if (m_camera != null)
{
Camera.main.AddCommandBuffer(CameraEvent.AfterEverything, m_commandBuffer);
}
}
}
private void Update()
{
if (m_stateMachine != null)
{
m_stateMachine.Advance(Time.deltaTime);
}
}
private void OnDisable()
{
if (m_camera != null && m_commandBuffer != null)
{
m_camera.RemoveCommandBuffer(CameraEvent.AfterEverything, m_commandBuffer);
}
}
}
```
1. Create a Unity [RenderTexture](https://docs.unity.cn/ru/2020.1/Manual/class-RenderTexture.html) and [Material](https://docs.unity3d.com/2019.3/Documentation/Manual/Materials.html) in Assets
2. Assign the **RenderTexture** to the **Material**
3. Drag this behaviour to a **GameObject** and attach the material
4. Link the .riv asset and **RenderTexture** on the **RiveTexture** (custom script) behaviour
# Getting Started
Source: https://rive.app/docs/game-runtimes/unity/getting-started
Adding Rive to your Unity project.
New to Rive? Start with the [Rive Editor](/docs/editor/fundamentals/overview) to create your graphics, then [export for runtime](/docs/editor/exporting/exporting-for-runtime) when you're ready to bring them into Unity.
Create, rig, and build interactive Rive graphics in the editor.
Supported Unity versions, graphics backends, and feature support.
## Example Projects
To quickly experiment with Rive in Unity, run one of our [example projects](https://github.com/rive-app/rive-unity-examples).
## Installation
The Rive Unity package is available on the Unity Asset Store and on GitHub.
The **Unity Asset Store** has a simpler installation process and only includes the stable package releases. Installing from **GitHub** gives you access to stable releases as well as canary tags, which are updated more frequently and may include [newer/early access features](/docs/feature-support) before they land in a stable release.
Install the package from the Unity Asset Store:
* [Rive for Unity (Asset Store)](https://assetstore.unity.com/packages/tools/gui/rive-350858)
Open Window -> Package Manager
Select My Assets
Find Rive and select Download / Import
The [rive-unity package](https://github.com/rive-app/rive-unity) is also available to install from GitHub using a git dependency.
Add it via **Window -> Package Manager** and choose **Add package from git URL...** (replace `0.0.0` with the [latest release](https://github.com/rive-app/rive-unity/releases)):
```bash theme={null}
https://github.com/rive-app/rive-unity.git?path=package#v0.0.0
```
Paste the URL with a version tag
Unity can occasionally crash when upgrading the packages while the Editor is running. If you experience this, close Unity first, update the version in `Packages/manifest.json`, then reopen the project.
You can add it manually to your project's `Packages/manifest.json` (replace `0.0.0` with the [latest release](https://github.com/rive-app/rive-unity/releases)):
```json theme={null}
"app.rive.rive-unity": "https://github.com/rive-app/rive-unity.git?path=package#v0.0.0"
```
## Adding a Rive file to Unity
See our documentation on [Exporting](/docs/editor/exporting/exporting-for-runtime) graphics for runtime.
Once you have a `.riv` file, drag it into the Unity **Project** window. Unity will import it and automatically create a **Rive Asset** you can reference from components and scripts.
On the [Marketplace](https://rive.app/marketplace), you can find Rive files that can be remixed.
## Displaying a Rive File
**Drag-and-Drop**
To display a Rive file in UI, drag it into the **Scene Hierarchy**. This creates a screen-space setup inside a uGUI Canvas.
The `com.unity.ugui` package must be installed to use these components. This is usually included by default in new Unity projects.
To display a Rive file on a mesh, drop the Rive file onto an existing GameObject with a `MeshRenderer`. This creates a **Rive Panel** and adds a **Rive Texture Renderer** component to the mesh GameObject.
**Quick Creation Menu**
Right-click in the scene hierarchy to create the following:
* `Rive > Rive Panel` - Creates a standalone panel
* `Rive > Rive Panel (Canvas)` - Creates a UI-ready panel
* `Rive > Widgets > Rive Widget` - Adds a standard Rive widget
## Next steps
How files, artboards, and state machines are represented in Unity.
Rive Panel, Rive Widget, and recommended component-based workflows.
Bind your C# scripts to your Rive graphics for UI and gameplay-driven updates.
Performance and usage considerations for Rive in Unity.
# Layouts
Source: https://rive.app/docs/game-runtimes/unity/layouts
Control the layout of your Rive animation in Unity
For more information on creating Rive Layouts, see the [editor documentation](/docs/editor/layouts/layouts-overview).
## The Fit Mode
A Rive graphic authored in the editor will not necessarily match the size of the container it is rendered into at runtime. We need to determine the behavior for this scenario, as no one size fits all.
The solution is choosing the fit mode. This is specified on the container and controls how Rive is scaled.
* `Layout`: Use the Rive layout engine to apply responsive layout to the artboard, matching the container dimensions. For this to work, the artboard must be designed with layouts in mind. See [Responsive Layouts](#responsive-layouts) for more information on how to use this option.
* `Contain`: **(Default)** Preserve aspect ratio and scale the artboard so that its larger dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will leave space on the shorter dimension's axis.
* `ScaleDown`: Preserve aspect ratio and behave like `Contain` when the artboard is larger than the container. Otherwise, use the artboard's original dimensions.
* `Cover`: Preserve aspect ratio and scale the artboard so that its smaller dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will clip the artboard on the larger dimension's axis.
* `FitWidth`: Preserve aspect ratio and scale the artboard width to match the container's width.
If the aspect ratios between the artboard and container do not match, this will result in either vertical clipping or space in the vertical axis.
* `FitHeight`: Preserve aspect ratio and scale the artboard height to match the container's height.
If the aspect ratios between the artboard and container do not match, this will result in either horizontal clipping or space in the horizontal axis.
* `Fill`: Do not preserve aspect ratio and stretch to the container's dimensions.
* `None`: Do not scale. Use the artboard's original dimensions.
For either dimension, if the artboard's dimension is larger, it will be clipped. If it is smaller, it will leave space.
### Alignment
In all options other than `Layout` and `Fill`, there is the possibility that the Rive graphic is clipped or leaves space within its container. Alignment determines how content aligns within the container. The following options are available.
* `TopLeft`
* `TopCenter`
* `TopRight`
* `CenterLeft`
* `Center` **(Default)**
* `CenterRight`
* `BottomLeft`
* `BottomCenter`
* `BottomRight`
## Applying the Fit and Alignment
Using a **Rive Widget** component, you can select from a list of **Fit** and **Alignment** options.
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
The **fit** and **alignment** can be controlled on the **Rive.Renderer** `.Align()` method:
```cs theme={null}
public Fit fit = Fit.contain;
public Alignment alignment = Alignment.Center;
public RenderTexture renderTexture;
private Rive.Renderer m_riveRenderer;
...
m_renderQueue = new Rive.RenderQueue(renderTexture);
m_riveRenderer = m_renderQueue.Renderer();
...
if (m_artboard != null && renderTexture != null)
{
m_riveRenderer.Align(fit, alignment, m_artboard);
m_riveRenderer.Draw(m_artboard);
}
```
## Responsive layouts
The `Layout` **Fit** mode lets you display resizable artboards with built-in responsive behavior, configured directly in the graphic. Set a **Fit** of type **Layout** at runtime and the artboard will resize automatically. Optionally, provide a **Layout Scale Factor** to further adjust the scale of the content.
When **Fit** is set to `Layout`, the **Rive Widget**:
• Measures the available space from its RectTransform.
• Calculates a new artboard size based on both the `Layout Scaling Mode` and a `Layout Scale Factor` .
• Dynamically resizes the artboard to match the calculated dimensions.
## Layout Scaling Modes
You can choose from three layout scaling modes:
**Reference Artboard Size (Default)**
• Scales the artboard proportionally based on its original (reference) size, preserving the same relative size across different resolutions.
• The artboard always appears “the same size in proportion to the screen,” maintaining consistent, resolution-agnostic visuals.
• Use the Layout Scale Factor to fine-tune or amplify the layout scaling above or below 1×.
**Constant Pixel Size**
• The artboard maintains its pixel size, regardless of the screen resolution or DPI.
• The Layout Scale Factor is a direct multiplier on the pixel size of the original artboard.
• This mode can cause the artboard to appear larger on lower-resolution screens and smaller on higher-resolution screens.
**Constant Physical Size**
• Attempts to maintain the artboard’s physical dimensions across different devices by scaling according to DPI.
• A device with a higher DPI will see larger pixel scaling so that, physically, the artboard is the same size from device to device.
• Requires two additional properties in RiveWidget:
* Fallback DPI: Used if Screen.dpi is unavailable.
* Reference DPI: The baseline DPI for your UI (e.g., 96 if you’re targeting standard desktop size).
## Layout Scale Factor
Regardless of which `Layout Scaling Mode` you select, you can further scale the artboard via the `Layout Scale Factor` . A value of 1.0 means no additional scaling; values greater than 1.0 enlarge the artboard, and values below 1.0 shrink it.
In practice, you might use this factor to give yourself flexibility in adjusting the artboard size, even after choosing a particular scaling mode. For example, you might find that everything is slightly too large on mobile and set the Layout Scale Factor to 0.9 (90% of the scaled size).
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
**Implementing Layout in Custom Scripts**
When implementing `Fit.Layout` in your custom scripts, consider the following aspects:
1. **Screen Resolution and Scaling**
* Monitor screen resolution changes
* Handle DPIs
* Implement proper scaling for different display densities
2. **Input Handling**
* Transform input coordinates to match the scaled layout
* Account for different DPIs when processing touch/mouse input
* Consider hit-testing adjustments for scaled elements This [script](https://github.com/rive-app/rive-unity/blob/main/examples/basic/Assets/GameRuntime/RiveScreen.cs) shows one way you could implement `Fit.Layout` support while considering the points mentioned above.
# Listeners
Source: https://rive.app/docs/game-runtimes/unity/listeners
Enable listeners on your Rive animation in Unity
For more information on Rive Listeners see the [editor documentation](/docs/editor/state-machine/listeners).
} href="/editor/state-machine/listeners">
Listeners give designers the tools to take their State Machine one step further and define click, hover, and mouse move actions that can change properties in the editor and at runtime without the need for a developer.
[Panel Renderers](/docs/game-runtimes/unity/components#panel-renderers) are responsible for passing pointer input to **Rive Panels.**
**Requirements**
* Set the `Pointer Input Mode` setting on any **Panel Renderer** to `Enable Pointer Input` if you want a **Rive Panel** to receive pointer events.
* Add an **EventSystem** to the scene. This provides input from Unity to the Panel Renderers and allows them to support any input system in Unity (as long as it uses the EventSystem)
* For the **Rive Canvas Renderer**, ensure the parent Canvas has a **Graphics Raycaster** attached.
* For the **Rive Texture Renderer**, ensure the event camera has a **Physics Raycaster** component attached.
The GameObject with the **Rive Texture Renderer** attached must also have a **MeshCollider** attached.
### Hit Testing
Hit testing controls how pointer events interact with **Rive Widgets** and the content behind them. You can configure this behavior using the `Hit Test Behavior` setting on your **Rive Widget**:
* **Opaque**: The widget blocks all pointer events within its bounds, regardless of whether there's an interactive element (listener) at the pointer location. Content behind the widget won't receive any pointer events.
* **Translucent**: The widget only blocks pointer events where there's an interactive element (listener) at the pointer location. If no listener is hit, the event passes through to content behind the widget.
* **Transparent**: All pointer events pass through to content behind the widget, but Rive listeners still detect and respond to pointer events. This allows simultaneous interaction with both the widget and background content.
* **None**: The widget doesn't perform any hit testing and ignores all pointer events.
This flexibility allows you to create layered interactive experiences while controlling precisely how pointer events are handled at each layer.
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
## Pointer Positions
In rive-unity pointer (mouse/touch) events can be passed to an artboard to enable Rive Listeners. This is accomplished by translating the pointer position to an artboard's local coordinate.
For a complete example see the **getting-started** project in the [examples repository](https://github.com/rive-app/rive-unity-examples) and open a sample scenes:
* **DrawToCameraScene**: Pointer events on a camera
* **DrawToCubeScene**: Pointer events on a mesh
#### Camera Hit Test
See the **DrawToCameraScene** scene in the **getting-started** project from the [example repository](https://github.com/rive-app/rive-unity-examples).
This code snippet demonstrates translating mouse position on the camera to an artboard.
```csharp theme={null}
private Artboard m_artboard;
private StateMachine m_stateMachine;
...
Camera camera = gameObject.GetComponent();
if (camera != null)
{
Vector3 mousePos = camera.ScreenToViewportPoint(Input.mousePosition);
Vector2 mouseRiveScreenPos = new Vector2(
mousePos.x * camera.pixelWidth,
(1 - mousePos.y) * camera.pixelHeight
);
if (m_artboard != null && m_lastMousePosition != mouseRiveScreenPos)
{
Vector2 local = m_artboard.LocalCoordinate(
mouseRiveScreenPos,
new Rect(0, 0, camera.pixelWidth, camera.pixelHeight),
fit,
alignment
);
m_stateMachine?.PointerMove(local);
m_lastMousePosition = mouseRiveScreenPos;
}
if (Input.GetMouseButtonDown(0))
{
Vector2 local = m_artboard.LocalCoordinate(
mouseRiveScreenPos,
new Rect(0, 0, camera.pixelWidth, camera.pixelHeight),
fit,
alignment
);
m_stateMachine?.PointerDown(local);
m_wasMouseDown = true;
}
else if (m_wasMouseDown)
{
m_wasMouseDown = false;
Vector2 local = m_artboard.LocalCoordinate(
mouseRiveScreenPos,
new Rect(0, 0, camera.pixelWidth, camera.pixelHeight),
fit,
alignment
);
m_stateMachine?.PointerUp(local);
}
}
```
#### Mesh Hit Test
See the **DrawToCubeScene** scene in the **getting-started** project from the [example repository](https://github.com/rive-app/rive-unity-examples)
This code snippet demonstrates translating a [RaycastHit](https://docs.unity3d.com/ScriptReference/RaycastHit.html) on an object to an artboard's local coordinates.
The **GameObject** must have a **MeshCollider** attached.
```csharp theme={null}
void HitTesting()
{
Camera camera = Camera.main;
if (camera == null || renderTexture == null || m_artboard == null) return;
if (!Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
return;
Renderer rend = hit.transform.GetComponent();
MeshCollider meshCollider = hit.collider as MeshCollider;
if (rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null || meshCollider == null)
return;
Vector2 pixelUV = hit.textureCoord;
pixelUV.x *= renderTexture.width;
pixelUV.y *= renderTexture.height;
Vector3 mousePos = camera.ScreenToViewportPoint(Input.mousePosition);
Vector2 mouseRiveScreenPos = new(mousePos.x * camera.pixelWidth, (1 - mousePos.y) * camera.pixelHeight);
if (m_lastMousePosition != mouseRiveScreenPos || transform.hasChanged)
{
Vector2 local = m_artboard.LocalCoordinate(pixelUV, new Rect(0, 0, renderTexture.width, renderTexture.height), fit, alignment);
m_stateMachine?.PointerMove(local);
m_lastMousePosition = mouseRiveScreenPos;
}
if (Input.GetMouseButtonDown(0))
{
Vector2 local = m_artboard.LocalCoordinate(pixelUV, new Rect(0, 0, renderTexture.width, renderTexture.height), fit, alignment);
m_stateMachine?.PointerDown(local);
m_wasMouseDown = true;
}
else if (m_wasMouseDown)
{
m_wasMouseDown = false; Vector2 local = m_artboard.LocalCoordinate(mouseRiveScreenPos, new Rect(0, 0, renderTexture.width, renderTexture.height), fit, alignment);
m_stateMachine?.PointerUp(local);
}
}
```
# Loading Assets
Source: https://rive.app/docs/game-runtimes/unity/loading-assets
Out-of-band assets in Rive Unity.
Only **embedded** and **referenced** assets are supported in Rive Unity; **hosted** assets are not currently supported.
Only **png** and **jpeg** image assets are supported. Support for **webp** is in progress.
## Asset export options
Within the Rive Editor, you can select an asset (for example, an image or font) in the **Asset Panel** and configure the export option for that asset. A Rive file can have a mixture of **embedded**, **referenced**, and **hosted** assets.
**Embedded** assets are included with the exported `.riv` binary file, while **referenced** assets are packaged separately and must be linked at runtime. Using **referenced** assets enables you to reuse the same asset across multiple animation files or in other parts of your game. This reduces the size of your `.riv` file and the resources needed to run your animations that use a shared asset.
### Embedded Assets
Any asset marked as embedded will automatically be loaded, and you do not need to do anything to configure the asset.
By selecting the riv file you'll get information on the file's assets in the **Unity Inspector**. The example image above shows an embedded font called "Roboto Flex" with a size of 1MB.
### Referenced Assets
Referenced assets need to be linked at runtime. The rive-unity package automatically handles the linking by attempting to find assets within the same directory that match the correct **Name** + **ID** combination. If an asset that matches the criteria is discovered, that asset is automatically converted to a Rive asset and linked.
For an asset to be discoverable and linked, the riv file and asset must be in the same Unity directory.
#### Let's take a look at an example
When exporting your runtime file from the Rive Editor, the `.riv` file and referenced assets are exported in a zip.
The extracted zip has an `acqua_text.riv` file with a referenced asset named `Roboto Flex-887377.ttf`. The referenced asset file name breaks down as such:
* **Name:** Roboto Flex
* **ID:** 887377
Selecting both files (or the entire folder) and dragging them into the **Unity Assets** folder will automatically link the embedded font file - if the **Name** + **ID** matches what the `.riv` file expects.
In the **Unity Inspector** for the Rive file, you can note that the **Asset** for the "Roboto Flex" font has been linked, and the **Roboto Flex** font file has also been converted to a Rive asset.
This example automatically converted and linked the font file because the animation and font files were added simultaneously. Alternatively, you can add the asset file first and then the riv file.; this will result in the same outcome.
If an asset was added after the riv file, you'll need to manually reimport the riv file by **right-clicking** it and selecting **Reimport**.
#### Selecting an importer
Alternatively, you can set the desired Importer for an asset by selecting the correct option in the Inspector. This means you can make an asset a Rive Asset or change back an incorrectly converted asset.
#### Sharing Assets
Reusing the same asset in your Rive files should result in a consistent **Name** + **ID** generation when exporting from the Rive Editor.
If a matching asset is not found when importing in Unity, there is a fallback to converting and linking a file that matches only the **Name**. You can optionally use this approach to have more control over asset importing, as an asset filename can be renamed in the Rive Editor.
# Procedural Rendering
Source: https://rive.app/docs/game-runtimes/unity/procedural-rendering
Procedurally draw shapes and paths in Unity using Rive
In this section, you’ll learn how to create custom paint and path objects. This enables you to perform custom computed draw commands using the Rive Renderer.
## Overview
The following classes are available:
* **BlendMode**: determines how the source pixels are blended with the destination pixels.
* **Color**: determines the color of the shape.
* **Gradient**: determines the color gradient of the shape (`LinearGradient` or `RadialGradient`)
* **Paint**: describes how to draw a shape - see [**Paint**](#paint).
* **PaintingStyle**: determines if the shape is filled or stroked.
* **Path**: defines a shape's outline or a clipping mask - see [**Path**](#path).
* **StrokeCap**: determines how the path's endpoints are drawn.
* **StrokeJoin**: the kind of finish to place on the joins between segments.
### RenderQueue
Create a `Rive.RenderQueue` and a `Rive.Renderer`:
```csharp theme={null}
public RenderTexture renderTexture;
private Rive.RenderQueue m_renderQueue;
private Rive.Renderer m_riveRenderer;
...
m_renderQueue = new RenderQueue(renderTexture);
m_riveRenderer = m_renderQueue.Renderer();
```
Call `draw` on the render queue and pass in a `Path` and `Paint` object.
```csharp theme={null}
m_path = new Path();
m_paint = new Paint();
m_riveRenderer.Draw(m_path, m_paint);
```
### Path
A path is a series of drawing commands. The path is used to define a shape's outline or a clipping mask.
Create a new path:
```csharp theme={null}
m_path = new Path();
```
The `Path` class provides various methods to construct a path:
* `moveTo`: Moves the current point to the given point.
* `lineTo`: Adds a straight line from the current point to the given point.
* `circle`: Adds a circle to the path.
* `cubicTo`: Adds a cubic bezier curve to the path.
* `quadTo`: Adds a quadratic bezier segment that curves from the current point.
* `addPath`: Adds the sub-paths of path to this path, transformed by the provided matrix.
* `close`: Closes the path. This will draw a line from the current point to the first point in the path.
* `reset`: Resets the path to an empty state.
* `flush`: to flush the path to native memory.
### Paint
Paint is used to describe how to draw a shape.
Create a new paint:
```csharp theme={null}
m_paint = new Paint();
```
The paint describes the color, gradient, style, thickness, blend mode, stroke cap, and stroke join for a shape.
Call `.Flush()` to flush the paint to native memory. See the [Example ](/docs/game-runtimes/unity/procedural-rendering#example)below.
## Example
This example demonstrates drawing an animated triangle to a [RenderTexture](https://docs.unity3d.com/ScriptReference/RenderTexture.html).

Rive Unity: Procedural Rendering
The `MonoBehaviour` to create the above:
```csharp theme={null}
using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using Rive;
public class RiveProcedural : MonoBehaviour
{
public RenderTexture renderTexture;
private Rive.RenderQueue m_renderQueue;
private Rive.Renderer m_riveRenderer;
private CommandBuffer m_commandBuffer;
private Camera m_camera;
Path m_path;
Paint m_paint;
private void Start()
{
m_renderQueue = new RenderQueue(renderTexture);
m_riveRenderer = m_renderQueue.Renderer();
m_path = new Path();
m_paint = new Paint();
m_paint.Color = new Rive.Color(0xFFFF0000);
m_paint.Style = PaintingStyle.stroke;
m_paint.Join = StrokeJoin.round;
m_paint.Thickness = 20.0f;
m_riveRenderer.Draw(m_path, m_paint);
m_commandBuffer = new CommandBuffer();
m_commandBuffer.SetRenderTarget(renderTexture);
m_riveRenderer.AddToCommandBuffer(m_commandBuffer);
m_camera = Camera.main;
if (m_camera != null)
{
Camera.main.AddCommandBuffer(CameraEvent.AfterEverything, m_commandBuffer);
}
}
private void Update()
{
if (m_path == null)
{
return;
}
m_path.Reset();
float expand = Time.fixedTime * 10;
m_path.MoveTo(256, 256 - 100 - expand);
m_path.LineTo(256 + 50 + expand, 256 + 50 + expand);
m_path.LineTo(256 - 50 - expand, 256 + 50 + expand);
m_path.Close();
m_path.Flush();
m_paint.Thickness = (Mathf.Sin(Time.fixedTime * Mathf.PI * 2) + 1.0f) * 20.0f + 1.0f;
m_paint.Flush();
}
private void OnDisable()
{
if (m_camera != null && m_commandBuffer != null)
{
m_camera.RemoveCommandBuffer(CameraEvent.AfterEverything, m_commandBuffer);
}
}
}
```
### Additional Resources
See the **getting-started** project in the examples repository for a complete example and open the **ProceduralRenderingScene** scene.
# Runtime Asset Swapping
Source: https://rive.app/docs/game-runtimes/unity/runtime-asset-swapping
You can dynamically swap assets in your Rive animations at runtime using the `CustomAssetLoaderCallback`. This lets you change fonts, images, or audio files while your Rive file is playing, making it possible to create dynamic visuals.
## Setting Up Asset Loading
To swap assets at runtime, provide a callback when loading your Rive file:
```csharp theme={null}
File.Load(Asset asset, CustomAssetLoaderCallback customAssetLoaderCallback);
File.Load(TextAsset asset, CustomAssetLoaderCallback customAssetLoaderCallback);
File.Load(byte[] riveFileByteContents, CustomAssetLoaderCallback customAssetLoaderCallback);
```
Your callback will be invoked whenever the runtime needs to load an asset. The callback should return `true` if you've handled the asset loading, or `false` to let the runtime handle it using the default loading behavior.
Here's an example showing how you might swap a font at runtime:
```csharp theme={null}
private FontOutOfBandAsset m_fontOobAsset;
private File m_file;
FontEmbeddedAssetReference fontEmbeddedAssetReference
private bool OobAssetLoaderDelegate(EmbeddedAssetReference assetReference)
{
// Keep a reference to the fontEmbeddedAssetReference so we can update the font again later
fontEmbeddedAssetReference = assetReference as FontEmbeddedAssetReference;
if (fontEmbeddedAssetReference != null)
{
fontEmbeddedAssetReference.SetFont(m_fontOobAsset);
return true;
}
return false;
}
private void Start()
{
m_fontOobAsset.Load();
m_file = Rive.File.Load(asset, OobAssetLoaderDelegate);
// You could call fontEmbeddedAssetReference.SetFont() here again to change the font
}
private void OnDestroy()
{
m_fontOobAsset.Unload();
m_file.Dispose();
}
```
#### Overload With Fallback
An overload of `Rive.File.Load()` includes a `fallbackToAssignedAssets` parameter.
If set to `true `and your custom loader doesn't handle a certain asset, the runtime will look for references assigned to your Rive asset in the Unity Inspector and automatically load them if they exist. This is handy when you only want to replace some assets and rely on default references for the rest.
```csharp theme={null}
var file = File.Load(myRiveAsset, MyCustomAssetLoader, fallbackToAssignedAssets: true);
```
## Asset Types
You can swap these types of assets at runtime:
* `FontOutOfBandAsset`: For font files
* `ImageOutOfBandAsset`: For image files
* `AudioOutOfBandAsset`: For audio files
## Asset Reference Types
When handling assets in your callback, you'll need to check the type of the `EmbeddedAssetReference` before setting the asset. Each asset type has its own reference class with specific setter methods:
```csharp theme={null}
private bool AssetLoaderDelegate(EmbeddedAssetReference assetReference)
{
// Handle font assets
if (assetReference is FontEmbeddedAssetReference fontReference)
{
fontReference.SetFont(myFontAsset);
return true;
}
// Handle image assets
if (assetReference is ImageEmbeddedAssetReference imageReference)
{
imageReference.SetImage(myImageAsset);
return true;
}
// Handle audio assets
if (assetReference is AudioEmbeddedAssetReference audioReference)
{
audioReference.SetAudio(myAudioAsset);
return true;
}
return false;
}
```
## Memory Management
When working with runtime asset swapping, we recommend following these best practices:
1. Always call `Load()` on your Out-Of-Band asset before using it in the callback
2. Dispose of resources properly:
* Call `Unload()` on your assets when they're no longer needed
* Call `Dispose()` on the Rive file when you're done with it
## Creating Out-of-Band Assets Dynamically
If you have an asset that isn't in your Unity project at build time (for example, if you're loading an image from a CDN), you can create an out-of-band asset at runtime using the `OutOfBandAsset.Create()` method. Here, the `bytes` parameter should be the raw file data for the asset.
This works for fonts, images, and audio, as long as you use the appropriate type (`FontOutOfBandAsset`, `ImageOutOfBandAsset`, or `AudioOutOfBandAsset`).
```
byte[] imageBytes = /* fetched from a CDN or elsewhere */;
var myImageAsset = OutOfBandAsset.Create(imageBytes);
```
## Example
For a demo of runtime asset swapping in Unity, see the **Image Swapping** scene in the [Rive Unity Examples repository](https://github.com/rive-app/rive-unity-examples).
# State Machines
Source: https://rive.app/docs/game-runtimes/unity/state-machines
For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/docs/editor/state-machine).
A Rive state machine is a set of animation states and the transitions between them. At runtime there is limited ability to observe or modify the state directly. This is by design, as this would limit the ability of a designer in Rive to modify the state machine without creating breaking changes. Instead, state machines are indirectly controlled through transitions conditioned on Data Binding properties.
A designer assigns a default state machine for each artboard in the Rive editor. They may create multiple state machines, each representing a different configuration of states and transitions. When rendering a Rive file and artboard, you may choose which state machine to play. If no state machine is specified, the default state machine for that artboard is used.
## Controlling Playback
State machines play by "advancing" over time. This is done once per frame by the amount of time between frames. For example, for a graphic running at 60 frames per second, the state machine would be advanced by approximately 16.67 milliseconds (1/60th of a second) each frame. This advancing evaluates keyframes, transitions, data bindings changes, and ultimately the visible artboard elements to create the illusion of motion over time.
This runtime provides a way to control whether the state machine is playing. When paused or stopped, the state machine does not advance and the last rendered frame remains visible. When playing from pause, the state machine resumes from where it left off, whereas when playing from stop, it restarts from the entry state.
In addition to the paused/stopped state, state machines may also "settle". This is an optimization where the Rive runtime detects that no further changes will occur (for example, if there are no active transitions or animations). While settled the state machine will also stop advancing. This improves performance and energy use by avoiding unnecessary calculations. State machines are unsettled by external actions that change their state, such as user input or data binding changes. You can additionally force a state machine to unsettle by calling play, though it may immediately re-settle if there is no further work to be done.
## Overview
A StateMachine advances (plays) animations in an Artboard.
A **Rive Widget** automatically loads and advances the state machine from [your artboard configuration settings](/docs/game-runtimes/unity/fundamentals#artboards). Here's how you can access the loaded state machine in your scripts:
```csharp theme={null}
[SerializeField] private RiveWidget m_riveWidget;
...
void OnEnable()
{
m_riveWidget.OnWidgetStatusChanged += OnWidgetStatusChanged;
}
private void OnWidgetStatusChanged()
{
// Wait for the Rive Widget to load before accessing the state machine.
if (m_riveWidget.Status == WidgetStatus.Loaded)
{
StateMachine m_stateMachine = m_riveWidget.StateMachine;
}
}
void OnDisable()
{
m_riveWidget.OnWidgetStatusChanged -= OnWidgetStatusChanged;
}
```
Using the low-level API is no longer recommended. Please use the [Component API](/docs/game-runtimes/unity/components) instead for ease of use and maintainability. This content is provided for legacy support only.
State Machines are instantiated from an Arboard instance:
```csharp theme={null}
private StateMachine m_stateMachine;
...
m_stateMachine = m_artboard?.StateMachine(); // default state machine
m_stateMachine = m_artboard?.StateMachine(0); // state machine at index
m_stateMachine = m_artboard?.StateMachine("Name"); // state machine with name
```
The state machine is played by calling `advance` and passing in the delta time:
```csharp theme={null}
private void Update()
{
m_stateMachine?.Advance(Time.deltaTime);
}
```
# Control a health bar with data binding in Unity
Source: https://rive.app/docs/game-runtimes/unity/tutorials/health-bar
Learn how to connect Unity gameplay data to a Rive health bar using view model instance properties.
Beginner
This tutorial walks through connecting a Rive health bar file to Unity using [Data Binding](/docs/editor/data-binding/overview). In this setup, your Unity code updates a single number (`health`) while the Rive file handles the visuals in response (e.g. color changes, and low-health warnings).
To view the completed project, see [HealthBar Demo](https://github.com/rive-app/rive-unity-examples/blob/main/getting-started/Assets/Demos/HealthBar).
## What you'll build
* A scene that displays a Rive health bar file in screen space
* A `HealthBarController` script that:
* Sets a `health` **number** property on the view model instance
* Listens for a `gameOver` **trigger** fired from the Rive file
* (Optional) A small keyboard control script to test damage/heal
## Requirements
* A Unity project with the Rive package installed (see [Getting Started](/docs/game-runtimes/unity/getting-started))
* Familiarity with view models and [data binding](/docs/game-runtimes/unity/data-binding) in Rive
* A `.riv` file that exposes a view model instance with:
* `health` (Number)
* `gameOver` (Trigger)
For this tutorial, you can use the demo health bar file available on the [Marketplace](https://rive.app/marketplace/24637-46037-health-bar-data-binding-quick-start/). Click **Download** to get the `.riv` file, or click **Preview in Rive** to see how it was set up in the Rive Editor.
## 1. Import the Rive file
Drag your `.riv` file into the Unity **Project** window. Unity will import it and create a **Rive Asset**.
## 2. Display the Rive file in UI
Create a new scene if you haven't already, then drag the **Rive Asset** into the **Scene Hierarchy** to create a Rive Panel/Widget that renders to a uGUI Canvas.
This will also add an **Event System** to the scene, which is required for the Rive Widget to receive pointer events.
## 3. Configure the Rive Widget
The **Rive Widget** should already be configured with the default artboard, state machine, and **Auto Bind Default** mode. If these aren't set correctly, configure them in the inspector:
Select the correct **Artboard Name** and **State Machine Name** for your health bar file.
For this tutorial, the selected artboard name should be `health_bar_v01` and the state machine name should be `State Machine 1`.
Set **Data Binding Mode** to **Auto Bind Default**.
The demo file was built with responsiveness in mind, so we also set the `Fit` mode to `Layout` to ensure the health bar is scaled to the size of the widget. See [Layout](/docs/game-runtimes/unity/layouts) to learn more.
## 4. Add the controller script
Create a C# script named `HealthBarController.cs`. This is a wrapper script that will be used to control the health bar from your gameplay code.
Why use a wrapper script? Even though you *can* access Rive view model instance properties anywhere in your project, it's usually better to keep that logic in one place because:
* **It keeps the rest of your code Rive-agnostic**: your gameplay scripts can call `Damage()` / `Heal()` without knowing about view models, property names, or widget lifecycle.
* **It centralizes Rive-specific details**: if the Rive file changes (renamed properties, new triggers, different setup), you update one script instead of hunting through your project.
### Step 4.1: Set up the basic fields
Start by adding references to the widget and the property names we'll look up in the Rive file.
```csharp theme={null}
using System;
using Rive;
using Rive.Components;
using UnityEngine;
using UnityEngine.Events;
public class HealthBarController : MonoBehaviour
{
[Header("Rive")]
[Tooltip("The Rive Widget that is displaying your health bar file.")]
[SerializeField] private RiveWidget m_riveWidget;
[Header("Initial health")]
[Tooltip("Initial health applied when the widget finishes loading.")]
[SerializeField] private float m_startingHealth = 100f;
[Header("ViewModel Property Names")]
[Tooltip("ViewModel Number property name used by the Rive file.")]
[SerializeField] private string m_healthPropertyName = "health";
[Tooltip("ViewModel Trigger property name fired by the Rive file.")]
[SerializeField] private string m_gameOverPropertyName = "gameOver";
// Cached references to the view model instance properties we'll be using to interact with the Rive file.
private ViewModelInstanceNumberProperty m_healthProperty;
private ViewModelInstanceTriggerProperty m_gameOverProperty;
// Track whether we've initialized health so we don't overwrite it if the widget GameObject is disabled and re-enabled.
private bool m_hasInitialized;
}
```
If you're not sure where to find the values for the `m_healthPropertyName` and `m_gameOverPropertyName` fields, they refer to the names of the `health` and `gameOver` properties in the main view model defined in the Rive file.
### Step 4.2: Add events
Add UnityEvents so you can react to health changes and game over events (both in the Inspector and from code).
```csharp theme={null}
[Header("Events")]
[Tooltip("Invoked whenever health changes in the Rive file.")]
public FloatEvent OnHealthChanged = new FloatEvent();
[Tooltip("Invoked when the Rive file fires the gameOver trigger.")]
public UnityEvent OnGameOver = new UnityEvent();
[Serializable]
public class FloatEvent : UnityEvent { }
```
### Step 4.3: Grab the view model instance properties when the widget is loaded
We use `OnWidgetStatusChanged` to access the view model instance once the widget is loaded. If you try to access the view model instance before the widget is loaded, it will be null.
The `OnWidgetStatusChanged` callback also provides a safe window to set initial values before the first frame renders, ensuring they're visible immediately without delay.
Add the following methods inside `HealthBarController`:
```csharp theme={null}
private void OnEnable()
{
if (m_riveWidget == null)
{
Debug.LogError($"{nameof(HealthBarController)}: No RiveWidget assigned.", this);
return;
}
m_riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
// If the widget was already loaded before we subscribed, initialize immediately.
HandleWidgetStatusChanged();
}
private void OnDisable()
{
if (m_riveWidget != null)
{
m_riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
// Clean up event listeners to avoid duplicate subscriptions.
if (m_healthProperty != null)
m_healthProperty.OnValueChanged -= HandleHealthChangedFromRive;
if (m_gameOverProperty != null)
m_gameOverProperty.OnTriggered -= HandleGameOverTriggeredFromRive;
}
private void HandleWidgetStatusChanged()
{
if (m_riveWidget.Status != WidgetStatus.Loaded)
return;
ViewModelInstance viewModelInstance = m_riveWidget.StateMachine?.ViewModelInstance;
if (viewModelInstance == null)
{
Debug.LogError($"{nameof(HealthBarController)}: ViewModelInstance is null. " +
"Make sure Data Binding Mode is set to Auto Bind Default / Selected.", this);
return;
}
// Clean up old listeners first.
if (m_healthProperty != null)
m_healthProperty.OnValueChanged -= HandleHealthChangedFromRive;
if (m_gameOverProperty != null)
m_gameOverProperty.OnTriggered -= HandleGameOverTriggeredFromRive;
// Get the health property by name.
m_healthProperty = viewModelInstance.GetNumberProperty(m_healthPropertyName);
if (m_healthProperty == null)
{
Debug.LogError($"{nameof(HealthBarController)}: Number property '{m_healthPropertyName}' not found.", this);
return;
}
// Get the gameOver property by name.
m_gameOverProperty = viewModelInstance.GetTriggerProperty(m_gameOverPropertyName);
if (m_gameOverProperty == null)
{
Debug.LogError($"{nameof(HealthBarController)}: Trigger property '{m_gameOverPropertyName}' not found.", this);
return;
}
// Subscribe to changes from the Rive file for the health and gameOver properties.
m_healthProperty.OnValueChanged += HandleHealthChangedFromRive;
m_gameOverProperty.OnTriggered += HandleGameOverTriggeredFromRive;
// Set the initial health value only once
if (!m_hasInitialized)
{
m_healthProperty.Value = m_startingHealth;
m_hasInitialized = true;
}
}
// Rive lets you listen for events when properties change or triggers are fired. We invoke our UnityEvents to notify other scripts of the changes.
private void HandleHealthChangedFromRive(float newValue)
{
OnHealthChanged.Invoke(newValue);
}
private void HandleGameOverTriggeredFromRive()
{
OnGameOver.Invoke();
}
```
This tutorial file includes a default view model instance. If the widget's **Data Binding Mode** was set to **Manual** or if the file did not have a default view model instance, `m_riveWidget.StateMachine?.ViewModelInstance` would be null.
In this case, you would need to manually create and bind a new view model instance to the state machine.
Learn more about binding modes and how to configure them in the [Data Binding](/docs/game-runtimes/unity/data-binding#auto-binding-in-the-inspector) documentation.
### Step 4.4: Expose a simple API for gameplay code
Now we add methods that provide a clean interface for gameplay without having to expose the underlying Rive implementation details.
First, add a `Health` property that reads from the `health` view model instance property when available:
```csharp theme={null}
///
/// Health is a thin wrapper over the view model number property.
/// Until the widget is loaded, it returns the starting value.
///
public float Health
{
get
{
// If the widget is loaded, read from the Rive view model instance property
if (m_healthProperty != null)
{
return m_healthProperty.Value;
}
// Widget isn't loaded yet, return the starting value
return m_startingHealth;
}
}
private void WriteHealth(float value)
{
// If the widget is loaded, write to the Rive view model instance property
if (m_healthProperty != null)
{
m_healthProperty.Value = value;
return;
}
// Widget isn't loaded yet. Store it so we can apply it when the view model instance is ready.
m_startingHealth = value;
}
```
Then add gameplay methods that use this helper:
```csharp theme={null}
public void Damage(float amount)
{
WriteHealth(Health - amount);
}
public void Heal(float amount)
{
WriteHealth(Health + amount);
}
```
## 5. Set up the controller in the Inspector
Attach `HealthBarController` to any GameObject (typically the same object as your `Rive Widget`), then:
Assign the `Rive Widget` field to the one you created earlier.Set `Starting Health` to the initial health value you want to display (for example, 70).
(Optional) Wire `On Game Over` to your own gameplay logic.
### Optional: keyboard controls
If you want a quick way to test this in Play Mode, you can drive the controller with keyboard input.
Create a script named `HealthBarKeyboardControls.cs`:
```csharp theme={null}
using UnityEngine;
using UnityEngine.InputSystem;
public class HealthBarKeyboardControls : MonoBehaviour
{
[SerializeField] private HealthBarController m_healthBar;
[SerializeField] private float m_damageAmount = 10f;
[SerializeField] private float m_healAmount = 20f;
private void Update()
{
var keyboard = Keyboard.current;
if (keyboard == null)
return;
// Damage (left arrow key )
if (keyboard.leftArrowKey.wasPressedThisFrame)
m_healthBar.Damage(m_damageAmount);
// Heal (right arrow key)
if (keyboard.rightArrowKey.wasPressedThisFrame)
m_healthBar.Heal(m_healAmount);
}
}
```
Attach `HealthBarKeyboardControls` to any GameObject, assign your `HealthBarController`, then press:
* `←` (left arrow key) to take damage
* `→` (right arrow key) to heal
This uses Unity's **Input System** package (`com.unity.inputsystem`). If your project is still using the legacy input manager, you'll need to enable the Input System in Unity (**Project Settings → Player → Active Input Handling**).
If the `Event System` in your scene shows a `Replace with InputSystemUIInputModel` button in the Inspector, click it to upgrade to the new Input System (otherwise pointer events won't work with the Rive file).
## 6. Drive health from gameplay code
From your gameplay scripts you can treat the Rive file like a "rendered UI" for your data:
* Call `Damage()` or `Heal()` based on gameplay events.
* Or read `Health` if you need the current value.
The data flow isn't just one-way. Your game scripts can also listen to triggers or changes to values from the Rive file. For example, your game manager script could listen to both health changes and the game over event:
```csharp theme={null}
public class GameManager : MonoBehaviour
{
[SerializeField] private HealthBarController healthBar;
private void Start()
{
// Listen to health value changes
healthBar.OnHealthChanged.AddListener(HandleHealthChanged);
// Listen to game over trigger
healthBar.OnGameOver.AddListener(HandleGameOver);
}
private void HandleHealthChanged(float newHealth)
{
Debug.Log($"Health changed to: {newHealth}");
// Your game logic...
}
private void HandleGameOver()
{
Debug.Log("Game Over!");
// Your game logic...
}
}
```
This approach separates your game logic from visual presentation. As long as you keep the data contract the same (the `health` number and `gameOver` trigger), you can change the health bar's appearance in the Rive Editor as much as you want without updating or writing any additional code. Simply swap out the old .riv file with the new one.
```csharp theme={null}
using System;
using Rive;
using Rive.Components;
using UnityEngine;
using UnityEngine.Events;
public class HealthBarController : MonoBehaviour
{
[Header("Rive")]
[Tooltip("The Rive Widget that is displaying your health bar file.")]
[SerializeField] private RiveWidget m_riveWidget;
[Header("Initial health")]
[Tooltip("Initial health applied when the widget finishes loading.")]
[SerializeField] private float m_startingHealth = 100f;
[Header("ViewModel Property Names")]
[Tooltip("ViewModel Number property name used by the Rive file.")]
[SerializeField] private string m_healthPropertyName = "health";
[Tooltip("ViewModel Trigger property name fired by the Rive file.")]
[SerializeField] private string m_gameOverPropertyName = "gameOver";
[Header("Events")]
[Tooltip("Invoked whenever health changes in the Rive file. It is called with the new health value.")]
public FloatEvent OnHealthChanged = new FloatEvent();
[Tooltip("Invoked when the Rive file fires the gameOver trigger.")]
public UnityEvent OnGameOver = new UnityEvent();
[Serializable]
public class FloatEvent : UnityEvent { }
private ViewModelInstanceNumberProperty m_healthProperty;
private ViewModelInstanceTriggerProperty m_gameOverProperty;
// Track whether we've initialized health so we don't overwrite it if the widget GameObject is disabled and re-enabled.
private bool m_hasInitialized;
public float Health
{
get
{
// If the widget is loaded, read from the Rive view model instance property
if (m_healthProperty != null)
{
return m_healthProperty.Value;
}
// Widget isn't loaded yet, return the starting value
return m_startingHealth;
}
}
private void WriteHealth(float value)
{
// If the widget is loaded, write to the Rive view model instance property
if (m_healthProperty != null)
{
m_healthProperty.Value = value;
return;
}
// Widget isn't loaded yet. Store it so we can apply it when the view model instance is ready.
m_startingHealth = value;
}
private void OnEnable()
{
if (m_riveWidget == null)
{
Debug.LogError($"{nameof(HealthBarController)}: No RiveWidget assigned.", this);
return;
}
m_riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;
// If the widget was already loaded before we subscribed, initialize the health bar immediately.
HandleWidgetStatusChanged();
}
private void OnDisable()
{
if (m_riveWidget != null)
{
m_riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;
}
// Clean up event listeners to avoid duplicate subscriptions.
if (m_healthProperty != null)
m_healthProperty.OnValueChanged -= HandleHealthChangedFromRive;
if (m_gameOverProperty != null)
m_gameOverProperty.OnTriggered -= HandleGameOverTriggeredFromRive;
}
private void HandleWidgetStatusChanged()
{
if (m_riveWidget.Status != WidgetStatus.Loaded)
return;
ViewModelInstance viewModelInstance = m_riveWidget.StateMachine?.ViewModelInstance;
if (viewModelInstance == null)
{
Debug.LogError($"{nameof(HealthBarController)}: ViewModelInstance is null. " +
"Make sure Data Binding Mode is set to Auto Bind Default / Selected.", this);
return;
}
// Clean up old listeners first.
if (m_healthProperty != null)
m_healthProperty.OnValueChanged -= HandleHealthChangedFromRive;
if (m_gameOverProperty != null)
m_gameOverProperty.OnTriggered -= HandleGameOverTriggeredFromRive;
// Get the health property by name.
m_healthProperty = viewModelInstance.GetNumberProperty(m_healthPropertyName);
if (m_healthProperty == null)
{
Debug.LogError($"{nameof(HealthBarController)}: Number property '{m_healthPropertyName}' not found.", this);
return;
}
// Get the gameOver property by name.
m_gameOverProperty = viewModelInstance.GetTriggerProperty(m_gameOverPropertyName);
if (m_gameOverProperty == null)
{
Debug.LogError($"{nameof(HealthBarController)}: Trigger property '{m_gameOverPropertyName}' not found.", this);
return;
}
// Subscribe to changes from the Rive file for the health and gameOver properties.
m_healthProperty.OnValueChanged += HandleHealthChangedFromRive;
m_gameOverProperty.OnTriggered += HandleGameOverTriggeredFromRive;
// Set the initial health value only once
if (!m_hasInitialized)
{
m_healthProperty.Value = m_startingHealth;
m_hasInitialized = true;
}
}
public void Damage(float amount)
{
WriteHealth(Health - amount);
}
public void Heal(float amount)
{
WriteHealth(Health + amount);
}
private void HandleHealthChangedFromRive(float newValue)
{
OnHealthChanged.Invoke(newValue);
}
private void HandleGameOverTriggeredFromRive()
{
OnGameOver.Invoke();
}
}
```
# Unity
Source: https://rive.app/docs/game-runtimes/unity/unity
Unity runtime for Rive.
See [Feature Support](#feature-support) below for an updated list of Rive features in Unity.
## Unity Version Support
The package supports Unity LTS versions from 2021 upwards (including Unity 6).
## Rendering Support
The rive-unity runtime uses the [Rive Renderer](https://rive.app/renderer) and is up to date with the latest C++ runtime version of Rive.
* [WebGL](https://github.com/rive-app/rive-unity/blob/main/WEBGL.md)
* Metal on Mac
* Metal on iOS
* D3D11 on Windows
* OpenGL on Windows
* OpenGL on Android
* Vulkan on Windows
* Vulkan on Android
* Vulkan on Ubuntu 24.04+ (x86\_64)
* D3D12
Planned support for:
* Consoles
### Bug Reports
If you encounter any errors or unexpected crashes while integrating the Rive Unity runtime, we recommend logging a detailed issue directly to the [rive-unity](https://github.com/rive-app/rive-unity/issues) repo with an **Editor.log** attached to the issue to help provide more details and context about what might have occurred.
You can find more details on where to find your Editor.log file in the [Unity docs](https://docs.unity3d.com/Manual/LogFiles.html).
Note that it is best to grab the Editor.log file immediately after a crash has occurred
## Feature Support
The rive-unity runtime uses the latest Rive C++ runtime. For more details on runtime support, see the [Feature Support](/docs/feature-support) page. Refer to the following table for what is currently supported in the Unity runtime.
# Getting Started
Source: https://rive.app/docs/game-runtimes/unreal/getting-started
Install the Rive Unreal plugin, run your first Artboard via URiveActorComponent, and display it in UMG.
This guide walks you through installing the Rive Unreal plugin, importing a `.riv` file, and displaying it in UMG.
**Prerequisites**
* Unreal Engine 5.6 or above.
* A C++ toolchain. The Rive plugin is a C++ plugin, so it's compiled as part
of your project regardless of how you install it:
* **Windows**: Visual Studio 2022 with the **Game development with C++**
workload (it includes the Windows SDK and .NET components Unreal needs).
* **macOS**: Xcode with the command-line tools (`xcode-select --install`).
## 1) Install the Plugin
Open the Epic Games Launcher or Unreal Editor.
Open Fab.
Search for Rive or go directly to:\
[https://www.fab.com/listings/3a2968c1-4a1d-427c-934e-92e4d8578b77](https://www.fab.com/listings/3a2968c1-4a1d-427c-934e-92e4d8578b77)
Add the plugin to your engine or project.
Restart Unreal Engine if prompted.
Clone the repository:
```bash theme={null}
git clone https://github.com/rive-app/rive-unreal.git
```
Copy the `Rive` plugin folder into your project's `Plugins/` directory.
## 2) Build the Project
The Rive plugin is C++, so your project must be compiled before the editor
can load it.
Right-click your `.uproject` file and choose **Generate Visual Studio
project files**.
Open the generated `.sln` in Visual Studio 2022.
Set the configuration to **Development Editor** / **Win64** and build.
Right-click your `.uproject` file and choose **Generate Xcode
project**.
Open the generated project in Xcode and build the editor target.
Alternatively, double-click the `.uproject` directly — if the plugin modules
aren't built, Unreal offers to rebuild them; click **Yes**.
## 3) Enable the Plugin
1. Open Edit → Plugins.
2. Locate Rive under Runtime.
3. Enable the plugin.
4. Restart the editor if required.
## 4) Import a Rive File
1. Open the Content Browser.
2. Drag and drop a `.riv` file into your project.
Unreal creates a **Rive File** asset. You can inspect this asset by double-clicking it.
A **Rive File** is an Unreal asset. It does not render directly.
The Unreal runtime does not yet support referenced assets. Be sure to embed all assets in the `.riv` file.
## 5) Create a Widget
1. Right-click on the **Rive file** asset and select **Create Rive Widget** from the context menu.
You can inspect the created widget by double-clicking it.
## 6) Create a Blueprint
1. You can now use the widget in a blueprint just as you would any other widget.
## 7) Bind a ViewModel
If the Rive File is not using **autobinding**, you must assign a viewmodel. This can be done in blueprints using the **Make View Model** node.
Your **Rive File** is now setup for use in your application. The next page will show you how to observe changes in the ViewModel.
# World-Space RenderTargets
Source: https://rive.app/docs/game-runtimes/unreal/in-world-textures
Drag a Rive RenderTarget into the level to create an in-world surface with an auto-generated material.
# World-Space RenderTargets
The fastest way to put Rive content on a mesh in world space is to use a **Rive RenderTarget** asset.
You can drag a **Rive RenderTarget** directly into the level. Unreal will:
* Spawn an actor in the scene
* Create a material that displays the RenderTarget texture
* Assign the material to the spawned mesh
This workflow is ideal for:
* In-world screens
* Monitors / kiosks
* Control panels
* “TV” surfaces
This page covers world-space RenderTargets.\
For screen-space UI (HUDs/menus), use **URiveWidget**.
## 1) Create a Rive RenderTarget asset
1. Rigt-click on a Rive file asset.
2. Select **Create Rive Render Target.**
Double clicking the created RenderTarget will open the asset editor in which you can specify:
* **Rive File**
* **Artboard**
* **State Machine** (optional)
* **Size / Resolution**
Start with 512×512. Increase only if the screen is large and close to the camera.
## 2) Drag it into the Level
Drag the RenderTarget from the Content Browser into the viewport to apply it to a mesh. Unreal will automatically create a material that samples the RenderTarget texture. At this point you should see the Rive content on the surface.
## 3) How updates work (Tick + Draw)
A RenderTarget is a texture output. It only changes when the runtime is advanced and drawn.
In practice, you must ensure there is a driver in your scene that:
* Advances the runtime (Tick)
* Draws the frame into the RenderTarget (Draw)
If nothing is driving the RenderTarget, it will appear static.
The recommended driver for RenderTargets is **RiveRenderTargetUpdater**, which is designed specifically for in-world textures.
## 4) Driving the RenderTarget with RiveRenderTargetUpdater
If you are using the RenderTarget workflow, place an Actor in the level that owns the runtime and renders to the target.
Typical setup:
1. Create an Actor Blueprint (e.g. `BP_RiveRenderDriver`)
2. Add a **RiveRenderTargetUpdater** component.
3. Point it at the **RenderTarget** in the details pane.
4. Set the RenderTarget size to match your in-world material needs.
**RiveRenderTargetUpdater** is intended for RenderTargets (world-space textures).\
It is not intended for screen-space widgets.
## 5) Interactivity
If your Rive content is interactive (state machine + view model):
* Bind a ViewModel
* Set ViewModel values
* Fire triggers
The RenderTarget will reflect changes the next time it is driven (tick/draw).
# Observing ViewModel Changes
Source: https://rive.app/docs/game-runtimes/unreal/observing-viewmodel-changes
React to animation and state updates by observing ViewModel property changes.
# Observing ViewModel Changes
In the Unreal runtime, **ViewModels** are the supported way for Rive content to communicate back to Unreal.
Legacy **State Machine** events and direct callback mechanisms are deprecated.\
New integrations should observe **ViewModel** property changes instead.
All runtime output from Rive should flow through a **ViewModel Instance**.
## How Observation Works
During the **Artboard** tick:
1. Unreal sets **ViewModel** values
2. The **State Machine** evaluates transitions
3. The **State Machine** may modify **ViewModel** values
4. Property change callbacks are emitted
5. Rendering occurs
Observation happens synchronously during this update cycle.
## Registering Callbacks
Each property on a **ViewModel Instance** can be observed.
When a property changes:
* The callback is invoked
* The updated value is available
* Logic can react immediately
Callbacks should be:
* Registered after creating the **ViewModel Instance**
* Unregistered before destroying the instance
* Owned by the same system that owns the instance
You can use the **Add Field Value Changed Delegate** to trigger events when a value is changed. The following image shows an example of a delegate being added to a **ViewModel** field.
Use **Trigger Properties** for actions.\
Use boolean or numeric properties for persistent state.
## Observing Structured Data
Because **ViewModels** may contain nested structures:
* Nested **ViewModel Instances** can also be observed
* Changes propagate through the same callback mechanism
* Observation remains consistent regardless of hierarchy depth
This allows complex UI or gameplay state to remain structured and predictable.
## Lifetime and Safety
Observation follows the lifetime of the **ViewModel Instance**.
Important rules:
* Do not observe destroyed instances
* Unbind callbacks before destroying instances
* Do not assume callbacks persist after **Artboard** reinitialization
Callbacks are synchronous and not asynchronous events.
## Summary
**ViewModels** are both the input and output boundary of the runtime.
Unreal writes values.\
The **State Machine** evaluates.\
Unreal observes changes.
# Runtime Asset Swapping
Source: https://rive.app/docs/game-runtimes/unreal/runtime-asset-swapping
Swap image assets at runtime in Unreal via data binding.
## Overview
To change a Rive file's images at runtime in Unreal, bind a `UTexture` to a ViewModel image property exposed via data binding in the Rive editor.
The Rive renderer requires image textures to use **premultiplied alpha** (AlphaComposite-style blending). Textures authored with straight alpha can show:
* Dark or black fringes around transparent edges
* Incorrect compositing against the background
* Loss of alpha
* Overly bright / oversaturated images
Export or preprocess textures with premultiplied alpha before using them at runtime.
### Blueprint
Call **Set Image Value** (category *Rive → Data Binding*) on your `RiveViewModel` reference. Enter the image property name (as exposed on the ViewModel via data binding) and pick the source texture from the *In Image* dropdown.
### C++
```cpp theme={null}
#include "Rive/RiveViewModel.h"
// ViewModel is a URiveViewModel*, Texture is any UTexture*
ViewModel->SetImageValue(TEXT("MyImageProperty"), Texture);
```
`SetImageValue` accepts any `UTexture` and notifies the state machine that the bound property changed. The property name must match the image property as exposed on the ViewModel via data binding.
## Troubleshooting
* **Image doesn't update:** confirm the property name matches the ViewModel image property as exposed via data binding, and that your `RiveViewModel` is the one driving the artboard instance on screen.
* **Image looks wrong (dark fringes, incorrect alpha, oversaturation):** verify the source texture uses premultiplied alpha.
# Unreal Engine
Source: https://rive.app/docs/game-runtimes/unreal/unreal
Rive’s Unreal runtime allows you to render and control Rive animations natively inside Unreal Engine. It integrates directly with Unreal’s object system and rendering pipeline and is designed for real-time use cases.
## Supported Unreal Versions
* Unreal Engine 5.7.3 and above
## Supported Platforms
* Microsoft DirectX 11
* Microsoft DirectX 12
* PC Vulkan
* macOS
* visionOS (experimental)
* PlayStation 5
* Nintendo Switch
* Nintendo Switch 2
* Xbox Series X
* Steam Deck - Proton D3D11
Mobile platform support is planned but not yet available.
## Rendering Backends
Depending on platform and engine configuration, supported backends include:
* DirectX (Windows)
* Metal (macOS)
The Unreal runtime uses RHI for rendering backend integration.
Rendering is handled by Rive’s native renderer and coordinated with Unreal’s render thread.
## Feature Support
| Feature | Supported |
| --------------------------- | ---------- |
| Animation playback | ✅ |
| State machines | ✅ |
| ViewModels (data binding) | ✅ |
| Property observation | ✅ |
| Text rendering | ✅ |
| Image assets | ✅ |
| Multiple Artboards per file | ✅ |
| Scripting | ✅ |
| Legacy state machine events | Deprecated |
| Legacy direct inputs | Deprecated |
Legacy state machine events and inputs are deprecated.\
New integrations should use ViewModels for both input and output.
## Architecture Overview
The Unreal runtime is built around three core runtime objects:
* **Rive File** — Unreal asset containing imported Rive data
* **Artboard** — Runtime instance responsible for evaluation and rendering
* **ViewModel** — Typed data boundary between Unreal and Rive logic
The typical data flow is:
Unreal → ViewModel → State Machine → ViewModel → Unreal
State machines consume and modify ViewModel properties. Unreal observes those changes.
## Support & Community
If you need help:
* Join the Rive Community: [https://community.rive.app](https://community.rive.app)
# Using Triggers
Source: https://rive.app/docs/game-runtimes/unreal/using-triggers
Using triggers in the Rive plugin.
# Triggering Events
In the Unreal runtime, "events" are modeled as **ViewModel Trigger Properties**.
Fire and observe triggers through the **ViewModel Instance**.
Use **Trigger Properties** for one-shot actions (clicks, milestones, transitions).
## Recommended Flow
1. Define a **Trigger Property** in your Rive ViewModel (for example `OnClick`).
2. In Unreal, create and bind a **ViewModel Instance** to your Rive widget or artboard.
3. Fire the trigger from Blueprint using **Call **.
4. Respond to the trigger in Blueprint using **Bind Event to **.
## Blueprint Setup
1. Create a Rive widget from your imported `.riv` file.
2. Create a **ViewModel Instance** using **Make View Model**.
3. Assign that instance to the widget/artboard.
4. Keep a reference to the bound ViewModel instance.
Keep ViewModel creation and delegate binding in the same owning Blueprint so lifetime is clear.
## Firing a Trigger in Blueprint
When you want to fire an event (button press, gameplay action, etc.), call the trigger function exposed on the bound ViewModel instance.
Typical pattern:
1. Get your bound ViewModel instance reference.
2. Call **Call ** (for example `Call OnClick`).
3. Pass any required inputs for the generated function signature.
The trigger is consumed during the next artboard tick and resets automatically.
In the following image, the trigger "loaded" is being fired from Blueprint:
## Observing Trigger Results
If Unreal needs to react when the trigger is fired:
* Use **Bind Event to ** on the ViewModel instance.
* Handle callbacks synchronously during the update cycle.
* Unbind delegates before destroying the ViewModel instance.
In the following image, a custom event is bound to the trigger "loaded":
# Best Practices
Source: https://rive.app/docs/getting-started/best-practices
Editor & runtime performance and usage considerations.
Rive is built to efficiently play interactive graphics in the editor and at runtime in applications and games. However, poorly optimized animations can consume significant resources and cause poor performance, particularly on low-end devices. In the following sections, we will outline important considerations and tips for maintaining optimal performance and minimal resource utilization both during design/animate time in the Rive editor, as well as during runtime in applications.
We recommend continuous testing of your animations on your target devices/platforms.
## Design-time Considerations
See below for some techniques to employ in the Rive editor to keep Rive performant:
### Asset Optimizations
Image, audio, and font assets are often the biggest source of bloat in .riv files. Unoptimized assets increase download size and must be loaded into memory, which can lead to slowdowns—especially on lower-end devices.
Only assets used in artboards will be compiled for runtime. Items in the Assets panel that aren't used in an artboard will not increase the size of your .riv file.
#### Fonts
Font files often include thousands of glyphs you may not need, such as Greek letters, mathematical operators, and icons. To reduce the size of the exported font or .riv file, [select which glyphs to include](/docs/editor/text/fonts#glyph-%2F-script-selection).
#### Raster Image Sizes & Dimensions
It is crucial to ensure that the size of an image asset is appropriate for its usage. For instance, avoid using an image with large dimensions (e.g., 8192 x 7022) when it will be displayed in a small section of your artboard (e.g., sized 100 x 100).
Using large images can quickly consume device memory. This is particularly true on mobile devices where applications are more memory-constrained. Even if these images are compressed, their dimensions will still impact the amount of application memory used.
If you have a very large image and only part of it will be visible at any given time (ie; a scrolling background), consider breaking the image into smaller chunks or mixing raster and recreating part of it as a vector.
#### Raster Image Compression
Compression involves reducing the file size by employing various algorithms to discard some image data. You can compress images directly from the Rive editor, which means you maintain the original image but compress the images for runtime use. If the asset is embedded in the animation, this will reduce the file size of the exported riv.
For the smallest image file size and best performance, we recommend exporting assets using the WebP Format.
#### Vector Images
Be efficient with the number of vertices in your vector image. While a few extra vertices won’t have much of an impact, thousands might. Be especially careful when importing vectors that were generated with AI, converted from raster images, or were created in drawing apps.
### Layer Blend Modes for Web
Blend modes are particularly expensive on the web because WebGL doesn’t expose mechanisms for accessing the framebuffer. To apply a blend mode, Rive must copy rendered pixels into a separate texture before compositing, which introduces significant performance and memory overhead.
While there are efforts underway to improve this through new WebGL capabilities, these solutions are still pending broader support. Until then, it’s best to use blend modes sparingly in web projects to ensure optimal performance.
### Artboard Considerations
#### Clipped Artboards
Clipping artboards is generally fine, but if you're experiencing performance issues, it's worth minimizing their use. Clipping can be computationally expensive, as the renderer must evaluate every object—including component instances—to determine pixel visibility. Instead, consider applying clips to specific objects or groups within the artboard.
In most cases you can safely remove the clip from the main artboard itself, since nothing outside the Rive instance will be rendered at runtime.
#### Unused Artboards
Unused artboards are still included in the compiled .riv file and are parsed when the file is first loaded. This can lead to unnecessary memory usage and performance overhead, especially if the unused artboards contain complex animations or large assets. To keep your file lean and efficient, it's best practice to remove any artboards that aren't actively being used.
### Idle Animations
If you have idle animation states where the graphic remains static at a given state in a state machine, consider using a "one-shot" animation and ensuring that the timeline animation is not unnecessarily long. At runtime in a state machine, if no looping animations or active blend states are playing, the runtime will preemptively “pause” itself until a transition to another state. This is useful because resource consumption (i.e., CPU usage) may decrease to the point of making Rive impact on resources negligible in applications.
Scenarios: Icons, buttons, graphics that only animate based on user interaction, etc.
### Using Solos
A [Solo](/docs/editor/manipulating-shapes/solos) is similar to a group but with the added ability to toggle the rendering of nested objects. It functions like a radio button, deactivating other objects on the same level.
When you're looking to show or hide a specific object, using a Solo can be a more efficient since it stops the rendering of any non active objects.
Solos can also be an effective way to build skins for a character, but it's important to note that this should only be used if your skins require binding and weighting.
**Data Binding Artboards**\
When looking to build more complex skins (that don't require binding and weighting), or wanting to swap out widgets, icons, ect... data binding an artboard node is the most performant way to do this.\
\
In addition to only rendering the things you need, you also save yourself both memory and CPU usage by providing only the graphics you need.
### Blend States
Similar to the guidance in “Idle Animations”, ensure blend states transition out to other states, or move to an exit state when finished if possible. When blend states are activated at runtime, Rive will continually play the state machine, even if it is not necessary anymore. Providing some transition away from the blend state when complete ensures Rive has one less “active” animation to track when considering whether to self-pause at any point while playing the state machine.
## Runtime Considerations
See below for some techniques to employ when using Rive runtimes in application to keep Rive performant:
### Out-of-band Assets
See our documentation on [Loading Assets](/docs/runtimes/loading-assets). This feature allows you to dynamically load and replace assets (such as fonts, images, and audio) at runtime through code and provide the resources to your Rive graphic. This has the following benefits:
* Reduces the exported `.riv` binary size.
* Assets can be reused across multiple Rive files or other areas of your application.
* Assets can be preloaded and cached to be more readily available before displaying a Rive graphic.
* Assets can be swapped based on the users’ screen size and resolution, like in this [web JS example](https://codesandbox.io/p/sandbox/cool-dewdney-hlk5xl?file=%2Fsrc%2Findex.ts).
### Caching your .riv
If you're using the same Rive file in multiple places across your page or app, you can [cache the .riv](/docs/runtimes/caching-a-rive-file) file to improve performance. The key benefit of caching is that the file only needs to be parsed and decoded once. Creating new artboard instances from a cached, already-decoded file is significantly faster than decoding the file each time before instantiating an artboard.
### Pausing Programmatically
There are several cases where you may want to pause a state machine configured with Rive programmatically. By pausing the Rive graphic at runtime, you may notice that Rive’s impact on the application has negligible resource consumption (i.e., CPU).
1. Rive graphic is offscreen
a. If a Rive graphic is scrolled offscreen and does not need to continue playing, call the `pause` API on the respective runtime you’re using to prevent Rive from continuing to animate and consume resources when not needed.
b. Call the `play` API to continue playing Rive when the graphic needs to continue animating if back onscreen.
2. Accessibility
* If a user has set in device settings that they prefer reduced motion, you may want to read this property at runtime and programmatically either call `pause` or set `autoplay: false` with the Rive runtime to ensure these users have reduced motion when navigating the application. Alternatively, different artboards or state machines can be created and loaded at runtime that function differently.
3. State machine is idle with static graphics
a. `Pause` when the Rive graphic is idling while it waits for a transition in the state machine from user interaction, data resolving, etc.
### Low-end devices
Rive will try to run performantly across all browsers/devices, but if you can, test how your application performs on resource-constrained devices with your specific Rive graphics running. You may find that for a given screen, Rive files that include heavily-animated graphics might be overkill for what is truly needed and decide to display static Rive graphics (i.e., autoplay: false) or reduce the amount of Rive entities animating at any given point.
A strategy for lower-end devices could be to create an alternative artboard or state machine with reduced usage/motion, that can dynamically be loaded in at runtime when running on older devices.
# Introduction
Source: https://rive.app/docs/getting-started/introduction
Rive is where designers, animators, and developers build interactive experiences. Design, animate, and code in one place. What you build in the editor is what ships in your app, game, or website. No mockups, no prototypes, no handoff. The real thing.
## Rive at a Glance
Create vector graphics, responsive layouts, and reusable components, or import images, audio, fonts, and other assets from your favorite design tools.
Bring your designs to life with timelines, keyframes, and smooth interpolation.
Build interactive experiences that respond to user input, screen size, scrolling, and application state.
Connect animations, text, colors, layouts, and other properties to real-time data.
Use scripting to create custom interactions, procedural animation, and behaviors that go beyond the built-in tools.
Export once and use your interactive graphics across websites, mobile apps, games, videos, and more.
## Quick Start
New to Rive? This step-by-step quickstart walks through the core concepts of the editor while building a complete interactive project from start to finish.
# Add VAT/Tax ID to Your Invoice
Source: https://rive.app/docs/home/account-admin/account-overview/add-vat-tax-id-to-your-invoice
Need a tax invoice with your VAT or Tax ID for your records? Use our form and we'll email you an updated invoice.
Request a Tax Invoice
You'll need:
* Your original Stripe invoice/receipt number
* Invoice date and amount
* Your company's billing address
* Your VAT/Tax ID number
Your tax invoice will be emailed to you immediately.
***
## For Future Invoices
To have your tax info included automatically on all future receipts, update your billing details in Stripe:
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. Click "Manage Billing" to open the Stripe Customer Portal
3. Update your customer information with your Tax ID
4. Save changes
All future invoices will include your tax details automatically.
**Questions?** Contact support.
# Cancel a Former Employee's Plan
Source: https://rive.app/docs/home/account-admin/account-overview/cancel-a-former-employees-subscription
If an employee left your company but their Rive plan is still billing your card, we can help cancel it. You don't need access to their account.
## What We Can Do
* ✅ Cancel the plan
* ✅ Remove your payment method from their account
* ✅ Refund recent charges (typically 1-2 months after departure)
## What We Cannot Do
* ❌ Give you access to their account
* ❌ Transfer their files to your company
* ❌ Change workspace ownership
**Why:** Workspace ownership belongs to the account that created it, not the payment method. Even if your company card paid for it, the workspace is theirs. We don't override ownership without the account holder's consent.
If you need the files, you'll need to work that out directly with the former employee.
***
## How to Request Cancellation
Contact support with:
* Your company name
* The employee's name and email (if known)
* Last 4 digits of the card being charged
* Approximate date the employee left
We'll verify you control the payment method, cancel the plan, and remove your card. You'll get confirmation within 1-2 business days.
***
## Prevent This Next Time
Create a company-owned workspace and add employees as members. When someone leaves, you remove them—no plans to chase down, no files lost.
**Questions?** Contact support.
# Change Your Email or Password
Source: https://rive.app/docs/home/account-admin/account-overview/change-your-email-or-password
1. Go to [rive.app/account](https://rive.app/account?utm_source=docs\&utm_medium=content)
2. Make sure you're on the **Account** tab
**To change your email:**
* Click "Edit Profile" on the right
* Update your email
* Click "Save changes"
**To change your password:**
* Click "Change Password"
* Enter your new password
* Click "Save Password"
**Questions?** Contact support.
# Refunds
Source: https://rive.app/docs/home/account-admin/account-overview/refunds
## Our Policy
Per Rive's [Terms of Service](/docs/legal/terms-of-service), fees are non-refundable. If you cancel your plan, you keep access until the end of your current billing period—we just won't renew it.
## Billing Errors
If you were charged incorrectly (duplicate charge, charged after cancellation, wrong amount), contact support with:
* Your account email
* Date and amount of the charge
* What went wrong
We'll investigate and fix any legitimate billing errors.
## Before You Upgrade
If you're not sure Rive is right for you:
* Use the free plan first to test it out
* Start with monthly instead of annual if you want flexibility
* Double-check you're upgrading the right workspace
**Questions?** Contact support.
# HTML Embed
Source: https://rive.app/docs/integrations/html-embed
Embed Rive in platforms that support custom HTML.
Use Rive in any platform that supports custom HTML embeds by pasting the **Embed Code** or **Embed Link** generated from the Rive editor.
This is useful for tools like **WordPress** and other platforms where you can add your own HTML, but a [dedicated Rive integration](/docs/integrations/overview) or [runtime](/docs/runtimes) is not available.
## Generate the embed code
In the Rive editor, click the **hamburger menu** in the top-left corner and select **Generate Embed URL**.
In the dialog that appears, click **Generate Embed URL**.
Once the URL is generated, select the option that best fits how you plan to use your file.
Some platforms (like Notion) support embedding directly from an **Embed link**. Otherwise, use the iframe **Embed code**.
For more information about the various types of embed URLs, see [Embed URLs](/docs/editor/embed-urls/overview#embed-url-options).
## Add the embed to your platform
## Need help?
Support for embeds depends on the platform you are using. For issues with adding custom HTML or iframes, refer to that platform’s documentation or support channels.
# 3rd Party Integrations
Source: https://rive.app/docs/integrations/overview
These integrations are maintained outside of Rive.
For help or support, please refer to the respective tool’s documentation or community.
## Website Builders
* **[Framer](https://www.framer.com/marketplace/plugins/rive/)** — Use the official Rive Framer plugin for direct integration
* **[Webflow](https://help.webflow.com/hc/en-us/articles/33961216978451-Embed-Rive-animations)** — Native support for Rive animations
## Video & Rendering
* **[Remotion](https://www.remotion.dev/docs/rive/)** — Render Rive animations to video programmatically
* **[Revideo](https://github.com/redotvideo/examples/tree/main/rive-explanation-video)** — Render Rive animations to video programmatically
## HTML Embed
Many platforms support custom HTML embeds or link-based embeds (for example, WordPress or Notion).
For these platforms, use the [HTML embed method](/docs/integrations/html-embed).
# .riv File Format
Source: https://rive.app/docs/runtimes/advanced-topic/format
The .riv file is the binary runtime format exported from the Rive Editor and used by all Rive runtimes.
## Runtime Format
The Rive editor exports your project as a .riv file for consumption by the Rive runtimes. This is a binary representation of your Artboards, Shapes, Animations, State Machines, etc. This is the file that Rive's runtimes read to display your content in an application, game, website, etc. The format was designed to provide a balance of quick load times, small file sizes, and flexibility with regards to future changes/addition of features.
### Binary Types
A binary reader for Rive runtime files needs to be able to read these data types from the stream.
Byte order is little endian.
| Type | Description |
| ------------------------- | ------------------------------------------------------------------------------- |
| variable unsigned integer | LEB128 variable encoded unsigned integer (abbreviated to varuint going forward) |
| unsigned integer | 4 byte unsigned integer |
| string | unsigned integer followed by utf-8 encoded byte array of provided length |
| float | 32 bit floating point number encoded in 4 byte IEEE 754 |
Reference Binary Readers
[C++ Reader](https://github.com/rive-app/rive-cpp/blob/master/src/core/binary_reader.cpp) [C++ Decoder](https://github.com/rive-app/rive-cpp/blob/master/include/rive/core/binary_reader.hpp)
### Header
The header is the first thing written into the file and provides basic information for the runtime to verify that it can read this file. A ToC (table of contents/field definition) is provided which allows the runtime to understand how it can skip over properties and objects it may not understand. This is part of what makes the format resilient to future changes/feature additions to the editor. An older runtime can at least attempt to load an older file and display it without the objects and properties it doesn't understand.
| Value | Type |
| ------------- | ---------------------- |
| Fingerprint | 4 bytes |
| Major Version | varuint |
| Minor Version | varuint |
| File ID | varuint |
| ToC | byte aligned bit array |
**Fingerprint**
The file fingerprint just lets the importer quickly sanity check that it's actually looking at a file exported by Rive. This is 4 bytes representing the utf8/ascii "RIVE". In a hex editor this looks like.
0x52 0x49 0x56 0x45/"RIVE"
**Major Version**
Runtimes are compatible with only a single major Rive export format version. The current major format is 7. If a runtime that supports format major 7 encounters a format major 6 file, it will immediately error and not attempt to read any further content, as the format is understood to be fundamentally different. This is provided as a last-resort tool for Rive to fundamentally change its export format if needed, and we try very hard to do this as rarely as possible.
The move from format 6 to format 7 happened years ago, and the editor no longer exports format 6 files.
If a future major format is introduced, runtimes and exports must match major format versions. In practice, this means:
* if you update to runtimes that support a new major format, you must re-export files to that major format
* if you stay on older runtimes, you must export files in the older compatible major format
* runtimes that add support for a new file format major version will also ship a major runtime version update
Example: if the Android runtime is currently major version `11`, and support for a new file format major is introduced, Android runtime support for that format will ship in a major `12` release.
To adopt it, you update to Android runtime `12` and re-export files to the new file format major.
If you need to verify a file's format version programmatically, read the `Major Version` and `Minor Version` values from the file header. Rive runtimes also surface a descriptive import error when the file format is incompatible.
Major versions are not cross-compatible. For example, a runtime that supports format version 6 cannot read format version 7 files, and vice versa.
Whenever a new major file format version is introduced, a corresponding major release is issued for all supported runtimes.
**Minor Version**
Minor version changes are compatible with each other provided the major version is the same. However, certain newer features may not be available if the runtime is of a different minor version. For example, major version 7 introduces the State Machine. We're working on adding new state types to the State Machine. A version 7.0 runtime may not be able to load all the states exported in a 7.1 file. However, the runtime will still be able to play the state machine, it'll simply not be able to do anything when it transitions to states it doesn't understand.
When a file uses newer features that an older compatible runtime does not support, the file still loads and supported features continue to work. Unsupported features are treated as no-ops. Rive runtimes are designed to avoid crashes or undefined behavior for this compatibility scenario.
Example Version Compatibility
| Runtime Version | File Version | Compatibility |
| --------------- | ------------ | ------------- |
| 6.1 | 6.0 | Yes |
| 6.1 | 6.2 | Yes |
| 6.1 | 7.0 | No |
| 7.0 | 6.1 | No |
| 7.0 | 7.1 | Yes |
#### File ID
This is a unique identifier for the file that in the future will be able to be used to distinguish the file by our API. The API isn't defined yet, but some of the planned features include re-exporting a newer version of the file on demand, getting details of the file, etc. For now this can be used to verify which file this export was generated from.
#### ToC
The Table of Contents section of the header is a list of the properties in the file along with their backing type. This allows the runtime to read past properties it wishes to skip or doesn't understand. It does this by providing the backing type for each property ID.
#### Field Types
There are 5 fundamental backing types but they are serialized in 4 different ways. Knowing how the type is serialized allows the runtime to know how to read it in. Even if it reads the wrong value or interprets it incorrectly, the important aspect is being able to read past it so the rest of the file can be read in safely.
For example, a boolean can be read as an unsigned integer as the backing type and serializer is compatible. Even though reading the boolean as an integer will not provide the valid value for the property, the runtime can still just read past it.
#### ToC Data
The list of known properties is serialized as a sequence of variable unsigned integers with a 0 terminator. A valid property key is distinguished by a non-zero unsigned integer id/key. Following the properties is a bit array which is composed of the read property count / 4 bytes. Every property gets 2 bits to define which backing type deserializer can be used to read past it.
The intention here is to provide the known property type keys and their backing type, such that if the property type is unknown, the reader can read the entirety of the value without under/over running the buffer.
| Backing Type | 2 bit value |
| ------------ | ----------- |
| Uint/Bool | 0 |
| String | 1 |
| Float | 2 |
| Color | 3 |
As an example, if there were a file with three known property types (property 12 a uint value, property 16 a string value, and 6 a bool value) the exporter would serialize data as follows:
varuint: 12
varuint: 16
varuint: 6
varuint: 0
2 bits: 0
2 bits: 1
2 bits: 0
Reference ToC Deserializers [Flutter](https://github.com/rive-app/rive-flutter/blob/bbee63bb6c791dcabd0cd9d9788ca7ec4783fddb/lib/src/rive_core/runtime/runtime_header.dart#L43-L60) [C++](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/include/runtime_header.hpp#L76-L104)
#### Baseline properties
Rive won't export properties that have been known to the system since the latest major version. We baseline when we shift new major versions as there will be no minor version that needs to read past newer properties. Newly introduced properties after the shift to the latest major will export as they are added and new minor versions are released.
## Content
The rest of the file is simply a list of objects, each containing a list of their properties and values. An object is represented as a varuint type key. It is immediately followed by the list of properties. Properties are terminated with a 0 varuint. If a non 0 value is read, it is expected to be the type key for the property. If the runtime knows the type key, it will know the backing type and how to decode it. The bytes following the type key will be one of the binary types specified earlier. If it is unknown, it can determine from the ToC what the backing type is and read past it.
### Core
All objects and properties are defined in a set of files we call core defs for [Core Definitions](https://github.com/rive-app/rive-cpp/tree/master/dev/defs). These are defined in a series of JSON objects and help Rive generate serialization, deserialization, and animation property code. The C++ and Flutter runtimes both have helpers to read and generate a lot of the boilerplate code for these types.
#### Object
A core object is represented by its Core type key. For example, a Shape has [core type key 3](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/dev/defs/shapes/shape.json#L4). Similarly you can see the generated code for the C++ runtime also [identifies a Shape with the same key](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/include/generated/shapes/shape_base.hpp#L12).
#### Properties
Properties are similarly represented by a Core type key. These are unique across all objects, so [property key 13](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/dev/defs/node.json#L16) will always be the X value of a Node object, and it [matches in the runtime](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/include/generated/node_base.hpp#L33). A Node's X value is known to be a floating point value so when it is encountered [it will be decoded as such](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/include/generated/node_base.hpp#L66-L68). Property key 0 is reserved as a null terminator (meaning we are done reading properties for the current object).
### Example Serialized Object
| Data | Type/Size | Description |
| ----- | ------------ | ------------------------------------------------------------------------- |
| 2 | varuint | object of type 2 (Node) |
| 13 | varuint | X property for the Node |
| 100.0 | 4 byte float | the X value for the Node |
| 14 | varuint | Y property for the Node |
| 22.0 | 4 byte float | the Y value for the Node |
| 0 | varuint | Null terminator. Done reading properties and have completed reading Node. |
### Context
Objects are always provided in context of each other. A Shape will always be provided after an Artboard. The Node's artboard can always be determined by finding the latest read Artboard. This concept is used extensively to provide the context for objects that require it. Another example, a KeyFrame will always be provided after a LinearAnimation, meaning you can always determine which LinearAnimation a KeyFrame belongs to by simply tracking that last read LinearAnimation.
### Hierarchy
Objects inside the Artboard can be parented to other objects in the Artboard. This mapping is more complex and requires identifiers to find the parent. The identifiers are provided as a [core def property](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/dev/defs/component.json#L28-L38). The value is always an unsigned integer representing the index within the Artboard of the ContainerComponent derived object that makes a valid parent.
For specifics around import context, you can review the ImportStack pattern used in the File reader. [Dart](https://github.com/rive-app/rive-flutter/blob/bbee63bb6c791dcabd0cd9d9788ca7ec4783fddb/lib/src/rive_file.dart#L101) [C++](https://github.com/rive-app/rive-cpp/blob/4512406300b7333ba543cd87930e67a24c2fc715/src/file.cpp#L137)
# Android
Source: https://rive.app/docs/runtimes/android/android
The Android runtime for Rive.
Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.
For more details, refer to the [feature support](/docs/feature-support/) and [choosing a renderer](/docs/runtimes/choose-a-renderer/) pages.
# Overview
Welcome to using Rive on Android. Rive runtime libraries are open-source, with Android available in the [rive-android](https://github.com/rive-app/rive-android) GitHub repository.
## The Two Android APIs
Rive for Android provides two main APIs for integrating Rive into your application.
**The New Compose API (Beta)**
This API is designed for Jetpack Compose, allowing for a more modern and declarative approach to building UIs. It is currently in beta and undergoing additional testing, and some issues may be uncovered during development. We will work to address these quickly as they come up. It is feature complete, production ready, and recommended for any new projects using Compose, while existing projects should migrate when feasible.
The entry point for this API is the `Rive` composable function, which can be used directly within your Compose UI code.
In addition to providing a more idiomatic Compose experience, this API is also powered by a stronger threading model with higher stability and flexibility to spread Rive work across multiple threads.
In the future this threading model will also include a View-based API for projects that cannot use Compose.
**The Legacy View-based API**
This API is based on Android Views and XML layouts. It has been widely deployed in production applications. It also supports [Compose by using AndroidView](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/LegacyComposeActivity.kt), though it does require some boilerplate to set up.
The entry point for this API is the `RiveAnimationView`, which can be added to your XML layouts or instantiated programmatically.
We refer to this as the "legacy" API as we are focusing our development efforts on the new Compose API going forward, though this API will continue to be supported and maintained for some time. For all new development projects, we recommend using the new Compose API. Additionally, we recommend migrating existing projects to the new API when feasible, as this version will be deprecated in the future and will have limited to no new feature development.
# Sample App
To explore the Android API, you can run our Android sample app. Each activity demonstrates a particular feature of the Rive Android runtime.
```bash theme={null}
git clone https://github.com/rive-app/rive-android
```
Open the cloned folder in Android Studio and select the `app` configuration and target device. Ensure that the build variant is set to `preview (default)` by opening the menu `Build - Select Build Variant...` and selecting the `preview (default)` variant for `app`.
The other build variants are for development purposes and require additional configuration. See [CONTRIBUTING.MD](https://github.com/rive-app/rive-android/blob/master/CONTRIBUTING.md).
# Getting Started
If you are looking for getting started instructions for the Legacy API, see [Getting Started (Legacy API)](/docs/runtimes/android/legacy-getting-started).
## Adding Rive to Your Project
Add the following dependencies to your `build.gradle` file in your project. We recommend using the latest version of the Rive Android runtime, which can be found on [Maven Central](https://central.sonatype.com/artifact/app.rive/rive-android).
```groovy theme={null}
dependencies {
...
implementation 'app.rive:rive-android:'
// For initialization, you may want to add a dependency on Jetpack Startup
implementation "androidx.startup:startup-runtime:1.1.1"
}
```
Rive needs to link and initialize its C++ runtime for its Kotlin bindings to work.
This can be done via an [initializer](https://developer.android.com/topic/libraries/app-startup) which does this automatically at app startup time. The initialization provider can be set up directly in your app's manifest file.
```xml theme={null}
```
Otherwise this can be achieved by calling the initializer in your code.
```kotlin theme={null}
AppInitializer.getInstance(applicationContext)
.initializeComponent(RiveInitializer::class.java)
```
If you want to initialize Rive yourself, this can be done in code using the following. This is the most flexible option, as you can lazily load the native library, but be sure to call it before any Rive functionality is used.
```kotlin theme={null}
Rive.init(context)
```
You can now add `Rive` composables to your Compose layouts. See the following sections for details. If you are using the legacy API, see [Getting Started (Legacy API)](/docs/runtimes/android/legacy-getting-started) instead.
## Enabling Logging
The Compose API uses extensive logging, especially at the debug level, to help diagnose issues. By default there is no logger enabled. Most commonly you will want to set the global logger to pipe to Android's Logcat, which has a convenience function as follows. This could be done in your activity's `onCreate` method or in your `Application` subclass, for instance, before any Rive functionality is used.
```kotlin theme={null}
RiveLog.logger = RiveLog.LogcatLogger()
```
## Adding Rive to Your Composition
The Compose API uses an explicit Rive worker which owns a thread for Rive operations, including file and asset decoding, advancing, and drawing. Before you can use any of the API surface, you must create a worker to host these operations. Be aware that the worker must live for the duration of all Rive resources made from it.
The simplest API within a Compose context is `rememberRiveWorker`, which will create and remember a worker for the lifetime of the composition. However, if you want to handle any unexpected errors during worker creation, you can use `rememberRiveWorkerOrNull`, which will return `null` if the worker could not be created and forward the error to an error state you provide.
```kotlin theme={null}
setContent {
// Simple
val riveWorker = rememberRiveWorker()
// Safe
val errorState = remember { mutableStateOf(null) }
val riveWorker = rememberRiveWorkerOrNull(errorState)
if (riveWorker == null) {
// Handle the error and early return
return@setContent
}
}
```
Before you can display a Rive element, you must load a Rive file, i.e. an exported .riv file. Rive files are specified by a `RiveFileSource`, which can be either a `RawRes` (for local raw resources) or a `Bytes` (for raw byte data, from any source including the network). In this case, we'll use a raw resource for simplicity. That source, along with the above worker, are passed to `rememberRiveFile`, which internally will load and cache the file for use in the composition.
```kotlin theme={null}
setContent {
... // riveWorker from above
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
}
```
The returned value is a `Result`, which can be either `Loading`, `Success`, or `Error`. This pattern requires you to handle the loading and error states appropriately in your UI. Typically this is done with a `when` statement.
```kotlin theme={null}
when (riveFile) {
is Result.Loading -> LoadingIndicator()
is Result.Error -> /* Handle error */
is Result.Success -> {
// The `RiveFile` is held in the `value` property
val file = riveFile.value
// Use the file - see next step
}
}
```
With the loaded `RiveFile` you can now display it using the `Rive` composable. The worker is captured in the Rive file, so those two components are all that are needed. Without specifying further, this will choose the default artboard and state machine, as specified in the Rive editor. Further options are explored in the other documentation sections, such as [Artboards](/docs/runtimes/artboards), [State Machines](/docs/runtimes/state-machines), and [Data Binding](/docs/runtimes/data-binding).
```kotlin theme={null}
when (riveFile) {
...
is Result.Success -> {
Rive(riveFile.value)
}
}
```
# Resources
[GitHub](https://github.com/rive-app/rive-android)
[Examples](https://github.com/rive-app/rive-android/tree/master/app/src/main/java/app/rive/runtime/example)
# API Reference
Source: https://rive.app/docs/runtimes/android/api-reference
# Artboards
Source: https://rive.app/docs/runtimes/android/artboards
Selecting which artboard to render at runtime
For more information on creating artboards in the Rive editor, please refer to [Artboards](/docs/editor/fundamentals/artboards).
## Choosing an Artboard
When a Rive object is instantiated or when a Rive file is rendered, you can specify the artboard to use. If no artboard is given, the [default artboard](/docs/editor/fundamentals/artboards#default-state-machine), as set in the Rive editor, is used. If no default artboard is set, the first artboard is used.
Only one artboard can be rendered at a time.
By default, the `Rive` composable will select and create the default artboard specified in the Rive editor. To specify a different artboard, you must first create an `Artboard` object from a loaded `RiveFile`.
### Compose
Artboard objects can be created in Compose using the `rememberArtboard` function, which takes a Rive file and name of the artboard.
```kotlin theme={null}
val artboard = rememberArtboard(myRiveFile, "My Artboard")
```
### Outside of Compose
Alternatively, you can create an artboard outside of Compose contexts. Note that with this approach you are responsible for managing the artboard's lifecycle and must eventually close it with `Artboard::close()` when no longer needed, or leveraging its [`AutoClosable`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-auto-closeable.html) interface with a `use` block.
```kotlin theme={null}
val artboard = Artboard.fromFile(myRiveFile, "My Artboard")
...
artboard.close()
```
### Using the Artboard
Once you have an artboard, you can pass it to the `Rive` composable via the `artboard` parameter.
```kotlin theme={null}
setContent {
Rive(
myRiveFile,
artboard = artboard
)
}
```
### Using XML Layouts
```xml theme={null}
```
### Using Kotlin
```kotlin theme={null}
animationView.setRiveResource(
R.raw.my_rive_file,
artboardName = "My Artboard",
autoplay = true
)
```
# Caching a Rive File
Source: https://rive.app/docs/runtimes/android/caching-a-rive-file
Under most circumstances a `.riv` file should load quickly and managing the `RiveFile` yourself is not necessary. But if you intend to use the same `.riv` file in multiple parts of your application, or even on the same screen, it might be advantageous to load the file once and keep it in memory.
## Example Usage
To cache a rive file in Android, you can use the Rive `File` class to load and cache the file. That `RiveFile` can then be reused across multiple `RiveAnimationView` instances. Here's a basic example:
```kotlin theme={null}
import app.rive.runtime.kotlin.RiveAnimationView
import app.rive.runtime.kotlin.RiveInitializer
import app.rive.runtime.kotlin.core.File
class MainActivity : ComponentActivity() {
var riveFile: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// Initialize Rive.
AppInitializer.getInstance(applicationContext)
.initializeComponent(RiveInitializer::class.java)
// Load Rive file from assets and cache.
application.assets.open("rewards_demo.riv").use { inputStream ->
val fileBytes = inputStream.readBytes()
riveFile = File(fileBytes)
}
setContent {
Row {
// First cached file usage.
AndroidView(
modifier = Modifier.weight(1f),
factory = { context ->
RiveAnimationView(context).also {
it.setRiveFile(
file = riveFile!!,
stateMachineName = "State Machine 1",
autoBind = true,
)
}
}
)
// Second cached file usage.
AndroidView(
modifier = Modifier.weight(1f),
factory = { context ->
RiveAnimationView(context).also {
it.setRiveFile(
file = riveFile!!,
stateMachineName = "State Machine 1",
autoBind = true,
)
}
}
)
}
}
}
override fun onDestroy() {
riveFile?.release()
super.onDestroy()
}
}
```
Please bear in mind that this is only one way to load the bytes, and your implementation may vary based on your app's architecture. The key point is to create a Rive `File` from the byte array and then set it on the `RiveAnimationView`.
A Rive `File` is reference counted and when created has a reference count of 1. Assigning it to a `RiveAnimationView` will keep an additional reference, but there is still the original reference from its creation. You are responsible for releasing that reference when you are done with the file by calling `File::release`. If you do not release the file, the native memory will remain until the app is closed, even if the Kotlin object is garbage collected.
# Data Binding
Source: https://rive.app/docs/runtimes/android/data-binding
Connect your code to bound editor elements using View Models
Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the [Overview](/docs/editor/data-binding/overview).
# 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](#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.
Unlike other runtimes, view models do not exist as a separate object in the Compose API. Instead, they are represented as a `ViewModelSource` sealed class that forms half of a builder pattern used to create view model instances. See [View Model Instances](#view-model-instances) for details on the other half - creating instances.
```kotlin theme={null}
// Named source
val vmSource = ViewModelSource.Named("My View Model")
// Default for artboard source
val vmSource = ViewModelSource.DefaultForArtboard(artboard)
```
```kotlin theme={null}
// `view` of type RiveAnimationView
view.setRiveResource(R.raw.my_rive_file)
val file = view.controller.file!!
// Get reference by name
val vm = file.getViewModelByName("My View Model")
// Get reference by index
for (i in 0 until file.viewModelCount) {
val indexedVM = file.getViewModelByIndex(i)
}
// Get reference to the default view model
val defaultVM = file.defaultViewModelForArtboard(view.controller.activeArtboard!!)
```
# 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:
| Type | Value |
| ----------------- | --------------- |
| Number | 0 |
| String | Empty string |
| Boolean | False |
| Color | 0xFF000000 |
| Trigger | Untriggered |
| Enum | The first value |
| Image | No image |
| Font | No font |
| Artboard | No artboard |
| List | Empty list |
| Nested view model | Null |
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.
In some samples, due to the wordiness of "view model instance", we use the abbreviation "VMI", as well as "VM" for "view model".
See [View Models](#view-models) for how to get a `ViewModelSource` to use below. With that, you can use the builder pattern to create a `ViewModelInstanceSource`, the second half. That source can then be passed to `rememberViewModelInstance` to create and remember the instance for the lifetime of the composition.
```kotlin theme={null}
// From previous section
val vmSource = ViewModelSource.Named("My View Model")
// Blank instance source
val vmiSourceBlank = ViewModelInstanceSource.Blank(vmSource)
// or
val vmiSourceBlank = vmSource.blankInstance()
// Default instance source
val vmiSourceDefault = ViewModelInstanceSource.Default(vmSource)
// or
val vmiSourceDefault = vmSource.defaultInstance()
// Named instance source
val vmiSourceNamed = ViewModelInstanceSource.Named(vmSource, "My Instance")
// or
val vmiSourceNamed = vmSource.namedInstance("My Instance")
// The completed source can now be used along with the Rive file to create and remember the instance
val viewModelInstance = rememberViewModelInstance(riveFile, vmiSourceNamed)
```
Additionally, you can reference a nested view model instance from within a parent instance using the `Reference` variant.
```kotlin theme={null}
val myVMI = rememberViewModelInstance(riveFile, mySource)
val referenceSource = ViewModelInstanceSource.Reference(myVMI, "Path/To/Nested VMI")
val nestedVMI = rememberViewModelInstance(riveFile, referenceSource)
```
```kotlin theme={null}
val vm = view.controller.file?.getViewModelByName("My View Model")!!
// Create blank
val vmiBlank = vm.createBlankInstance()
// Create default
val vmiDefault = vm.createDefaultInstance()
// Create by index
for (i in 0 until vm.instanceCount) {
val vmiIndexed = vm.createInstanceFromIndex(i)
}
// Create by name
val vmiNamed = vm.createInstanceFromName("My Instance")
```
### Binding
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.
See the [Compose data binding example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeDataBindingActivity.kt).
Binding to the state machine happens automatically when the `ViewModelInstance` is passed to the `Rive` composable.
```kotlin {6} theme={null}
val vmiSource = ViewModelSource.Named("My View Model").namedInstance("My Instance")
val vmi = rememberViewModelInstance(riveFile, vmiSource)
Rive(
riveFile,
viewModelInstance = vmi
)
```
See the [Legacy data binding example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/LegacyDataBindingActivity.kt).
```kotlin theme={null}
view.setRiveResource(
R.raw.my_rive_file,
artboardName = "My Artboard",
)
val vm = view.controller.file?.getViewModelByName("My View Model")!!
val vmi = vm.createInstanceFromName("My Instance")
// Apply the instance to the state machine (preferred)
view.controller.stateMachines.first().viewModelInstance = vmi
// Alternatively, apply the instance to the artboard
view.controller.activeArtboard?.viewModelInstance = 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.
Auto-binding does not exist in the Compose API due to the nature of composables. Since they are functions, it is difficult to get values out of them as compared to classes. Callbacks would require a null placeholder to remember the value before it has fired which creates more overhead than supplying the instance directly.
The equivalent is to create a view model instance with no source. This will internally create the default artboard, the default view model for that artboard, and the default instance for that view model. You can then pass that into the `Rive` composable.
```kotlin theme={null}
val vmi = rememberViewModelInstance(riveFile)
Rive(
riveFile,
viewModelInstance = vmi,
)
```
```kotlin {3} theme={null}
view.setRiveResource(
R.raw.my_rive_file,
autoBind = true,
)
```
# 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:
| Type | Supported |
| ---------------------- | --------- |
| Floating point numbers | ✅ |
| Booleans | ✅ |
| Triggers | ✅ |
| Strings | ✅ |
| Enumerations | ✅ |
| Colors | ✅ |
| Nested View Models | ✅ |
| Lists | ✅ |
| Images | ✅ |
| Artboards | ✅ |
For more information on version compatibility, see the [Feature Support](/docs/feature-support) page.
### Listing Properties
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.
Getting view model properties is a suspend operation, so it needs to be called from a coroutine scope such as `LaunchedEffect`.
```kotlin theme={null}
LaunchedEffect(riveFile) {
riveFile.getViewModelProperties("My View Model").forEach { property ->
Log.d("My Tag", "Property Name: ${property.name}, Type: ${property.type}")
}
}
```
```kotlin theme={null}
val vm = view.controller.file?.getViewModelByName("My View Model")!!
// A list of properties
val properties = vm.properties
assertContains(
properties,
ViewModel.Property(ViewModel.PropertyDataType.NUMBER, "My Number Property")
)
```
### Reading and Writing 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.
### Writing Values
The Compose API does not have explicit property objects. Instead, property values are set on the `ViewModelInstance` directly using methods which take their path.
```kotlin theme={null}
val vmi = rememberViewModelInstance(...)
vmi.setNumberProperty("Path/To/Property", 10f)
```
### Reading Values
Values are read throw a Kotlin [`Flow`](https://kotlinlang.org/docs/flow.html) which emits the latest value whenever it changes. You can collect this flow in a `LaunchedEffect` or convert it to a `State` using `collectAsState()` (or use `collectAsStateWithLifecycle()` to only collect during certain lifecycle states).
To get the latest value once without observing, you can use the terminal `first()` operator.
```kotlin theme={null}
val vmi = rememberViewModelInstance(...)
// Collect as State
val numberValue by vmi.numberPropertyFlow("Path/To/Property").collectAsState(initial = 0f)
Text(text = "Number value: $numberValue")
// Or collect
LaunchedEffect(vmi) {
vmi.numberPropertyFlow("Path/To/Property").collect { value ->
Log.d("Rive", "Number value changed: $value")
}
// Or get once
val numberValue = vmi.numberPropertyFlow("Path/To/Property").first()
Log.d("Rive", "Current number value: $numberValue")
}
```
```kotlin theme={null}
val vm = view.controller.file?.getViewModelByName("My View Model")!!
val vmi = vm.createInstanceFromName("My Instance")
val numberProperty = vmi.getNumberProperty("My Number Property")
// Get
val numberValue = numberProperty.value
// Set
numberProperty.value = 10f
```
### 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.
```kotlin theme={null}
val parent = rememberViewModelInstance(riveFile, ViewModelSource.Named("Parent VM").namedInstance("Parent"))
// Using references
val child = rememberViewModelInstance(riveFile, ViewModelInstanceSource.Reference(parent, "Child"))
val nestedNumber = child.numberPropertyFlow("My Nested Number").collectAsState(0f)
// Or using paths
val nestedNumber = parent.numberPropertyFlow("Child/My Nested Number").collectAsState(0f)
```
```kotlin theme={null}
val vm = view.controller.file?.getViewModelByName("My View Model")!!
val vmi = vm.createInstanceFromName("My Instance")
val nestedNumberByChain = vmi
.getInstanceProperty("My Nested View Model")
.getInstanceProperty("My Second Nested VM")
.getNumberProperty("My Nested Number")
val nestedNumberByPath = vmi
.getNumberProperty("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.
Observability is the default behavior when using the Compose API with Kotlin Flows. When you collect a property's flow, you will receive updates whenever the property's value changes.
```kotlin theme={null}
val vmi = rememberViewModelInstance(...)
val numberPropertyFlow = vmi.numberPropertyFlow("My Number Property").collectAsState(0f)
```
```kotlin theme={null}
val vm = view.controller.file?.getViewModelByName("My View Model")!!
val vmi = vm.createInstanceFromName("My Instance")
val numberProperty = vmi.getNumberProperty("My Number Property")
// Observe
lifecycleScope.launch {
numberProperty.valueFlow.collect { value ->
Log.i("MyActivity", "Value: $value")
}
}
// Or collect in Compose
val numberValue by numberProperty.valueFlow.collectAsState(0f) // 0 as the initial value while waiting for the first value
```
### Images
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.
See the [Compose data binding images example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeImageBindingActivity.kt).
To set an image property, you need an `ImageAsset`, which can be created from `rememberImage` using a byte array. The below example loads from raw resources into a `Result` for convenience, but you should use the pattern most appropriate for your app.
```kotlin theme={null}
val imageBytes by produceState>(Result.Loading) {
value = withContext(Dispatchers.IO) {
context.resources.openRawResource(R.raw.my_image)
.use { Result.Success(it.readBytes()) }
}
}
// `andThen` maps over the Result to only call the lambda if it's a Success, propagating Failure and Loading otherwise.
val image = imageBytes.andThen { bytes ->
rememberImage(riveWorker, bytes)
}
// Or combine into one statement
val image = produceState>(Result.Loading) {
value = withContext(Dispatchers.IO) {
context.resources.openRawResource(R.raw.my_image)
.use { Result.Success(it.readBytes()) }
}
}.value.andThen { bytes ->
rememberImage(riveWorker, bytes)
}
val vmi = rememberViewModelInstance(riveFile, ViewModelSource.Named("My View Model").defaultInstance())
LaunchedEffect(vmi, image) {
when(image) {
is Result.Failure -> { /* Handle failure to load image */ }
is Result.Loading -> { /* Handle loading state if needed */ }
is Result.Success -> {
// Set the image property value
vmi.setImage("Image property", image.value)
}
}
}
```
If you want to gate the presentation of your Rive content until the image is loaded and only if both are successful, you can use the `zip` convenience function to combine multiple `Result` objects together.
```kotlin theme={null}
val fileAndImage = riveFile.zip(image)
when (fileAndImage) {
is Result.Failure -> { /* Handle failure to load file or image */ }
is Result.Loading -> { /* Handle loading state if needed */ }
is Result.Success -> {
val (riveFile, image) = fileAndImage.value
// Both riveFile and image are loaded successfully here
// You can now present your Rive content and set the image property
}
}
```
For more information on image assets, see [Loading Assets](/docs/runtimes/android/loading-assets).
```kotlin theme={null}
// Load image from the assets folder.
val imageBytes = context.resources.openRawResource(R.raw.my_image).use { stream ->
stream.readBytes()
}
val vmi = it.stateMachines.first().viewModelInstance!!
// Replace image property in view model instance with new image.
val riveImage = RiveRenderImage.fromEncoded(imageBytes)
vmi.getImageProperty("Image property").set(riveImage)
```
### Lists
List properties let you manage a dynamic set of view model instances at runtime. For example, you can build a to-do app where users can add and remove tasks in a scrollable Layout.
See the [Editor section](/docs/editor/data-binding/lists) on creating data bound lists.
A single list property can include different view model types, with each view model tied to its own Component, making it easy to populate a list with a variety of Component instances.
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](/docs/editor/data-binding/lists#view-model-list-property) editor documentation.
See the [Compose data binding lists example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeListActivity.kt).
```kotlin theme={null}
val mainVMI = rememberViewModelInstance(riveFile)
val newListItem = rememberViewModelInstance(riveFile, ViewModelSource.Named("My Item VM").namedInstance("My List Item"))
LaunchedEffect(mainVMI, newListItem) {
val listProperty = "My List"
// Add new item to the end of the list
mainVMI.appendToList(listProperty, newListItem)
// Insert new item at index 0
mainVMI.insertToListAtIndex(listProperty, 0, newListItem)
// Swap items at index 0 and 1
mainVMI.swapListItems(listProperty, 0, 1)
// Remove specific instance
mainVMI.removeFromList(listProperty, newListItem)
// Remove item at index 0
mainVMI.removeFromListAtIndex(listProperty, 0)
}
```
Due to the dynamic nature of lists, you may need to create items within a coroutine rather than ahead of time with `rememberViewModelInstance`. Be aware that adding the same instance multiple times to a list will cause them all to share state, which may not be the desired behavior. Use the following pattern to create new instances as needed.
```kotlin theme={null}
val mainVMI = rememberViewModelInstance(riveFile)
LaunchedEffect(mainVMI) {
val listProperty = "My List"
// ⚠️ This must be `close`d, which is done here through `AutoCloseable.use`.
ViewModelInstance.fromFile(
riveFile,
ViewModelSource.Named("My Item VM").defaultInstance()
).use { item ->
mainVMI.insertToListAtIndex(listProperty, 0, item)
}
}
```
```kotlin theme={null}
// Acquire the default view model instance and the list property.
val vmi = animationView.file!!.firstArtboard.viewModelInstance!!
val listProperty = vmi.getListProperty("list")
// Create a view model instance for "First" and "Second" and add them to the list.
val firstInstance = animationView.file!!.getViewModelByName("My Item VM").createInstanceFromName("First")
listProperty.add(firstInstance)
val secondInstance = animationView.file!!.getViewModelByName("My Item VM").createInstanceFromName("Second")
listProperty.add(secondInstance)
// Swap the two items in the list.
listProperty.swap(0, 1)
// Remove both items from the list.
listProperty.remove(firstInstance)
listProperty.removeAt(0)
```
### Artboards
Artboard properties allows you to swap out entire components at runtime. This is useful for creating modular components that can be reused across different designs or applications, for example:
* Creating a skinning system that supports a large number of variations, such as a character creator where you can swap out different body parts, clothing, and accessories.
* Creating a complex scene that is a composition of various artboards loaded from various different Rive files (drawn to a single canvas/texture/widget).
* Reducing the size (complexity) of a single Rive file by breaking it up into smaller components that can be loaded on demand and swapped in and out as needed.
See the [Compose data binding artboards example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeArtboardBindingActivity.kt).
```kotlin theme={null}
val vmi = rememberViewModelInstance(mainFile)
val artboard = rememberArtboard(mainFile, "My Artboard")
LaunchedEffect(vmi, artboard) {
vmi.setArtboard("My Artboard Property", artboard)
}
```
```kotlin theme={null}
// Acquire the default view model instance and the artboard property.
val vmi = animationView.file!!.firstArtboard.viewModelInstance!!
val artboardProperty = vmi.getArtboardProperty("My Artboard Property")
// Set artboard from same file.
val localArtboard = animationView.file!!.getArtboard("My Artboard")
artboardProperty.set(localArtboard)
// Load external file if needed
val externalFile = File.load(context.assets, "external_file.riv")
// Set artboard from external file.
val externalArtboard = externalFile.getArtboard("My External Artboard")
artboardProperty.set(externalArtboard)
// Clean up external file when done
externalFile.dispose()
```
You can also create a bindable artboard with a view model instance to control its state.
```kotlin theme={null}
// Create the view model instance for the child artboard.
val childVmi = childFile.getViewModelByName("ChildVM").createBlankInstance()
val bindableArtboard = childFile.createBindableArtboardByName("Child", childVmi)
// Get the artboard property from the main artboard.
val vmi = rive.file!!.firstArtboard.viewModelInstance!!
val artboardProperty = vmi.getArtboardProperty("Artboard property")
// Set the bound artboard VMIs state.
childVmi.getNumberProperty("rotation").value = 90f
// Set the bound artboard on the main artboard.
artboardProperty.set(bindableArtboard)
// Release the reference we hold from creation.
bindableArtboard.release()
```
### 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.
```kotlin theme={null}
LaunchedEffect(riveFile) {
val enums = riveFile.getEnums()
Log.i("RiveEnums", "First enum name: ${enums[0].name}")
}
```
```kotlin theme={null}
val enums = view.controller.file?.enums!!
val firstEnumName = enums[0].name
val firstEnumFirstValue = enums[0].values[0]
```
# Examples
See the following examples:
* [Data Binding Overview](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeDataBindingActivity.kt)
* [Data Binding Images](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeImageBindingActivity.kt)
* [Data Binding Artboards](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeArtboardBindingActivity.kt)
* [Data Binding Lists](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeListActivity.kt)
See the [data binding overview example](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/LegacyDataBindingActivity.kt).
# Fonts
Source: https://rive.app/docs/runtimes/android/fonts
Loading and replacing fonts dynamically at runtime.
## Swapping Font Assets at Runtime
Fonts can be loaded dynamically at runtime. This allows you to localize your Rive content without increasing the file size of the exported .riv file.
Swapping a font asset replaces all instances of the font.
For more information, see [Loading Assets](/docs/runtimes/android/loading-assets).
## Fallback Fonts
When rendering text, not all glyphs (characters) may be available in the active font. This commonly occurs when:
* Using custom fonts that don’t support all languages or Unicode ranges
* The embedded font is a subset of the font
* User-generated or dynamic text contains unexpected characters
A fallback font is used automatically when the primary font cannot render a specific glyph. These are typically system fonts, which generally provide broad Unicode coverage.
On Android, font sizes specified for fallback fonts are ignored. Instead, the platform selects system fonts that best match the styling and animation of the text run at runtime.
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:
```kotlin theme={null}
fun getFont(weight: Fonts.Weight): List
```
Implementers need to override this method. The user's implementation must then be set as the current fallback strategy via `FontFallbackStrategy.stylePicker`.
**Example:**
```kotlin theme={null}
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 {
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**
```kotlin theme={null}
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:**
```kotlin theme={null}
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:**
```kotlin theme={null}
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:**
```kotlin theme={null}
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:**
```kotlin theme={null}
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:**
```kotlin theme={null}
val systemFonts = FontHelper.getSystemFonts()
```
# Layout
Source: https://rive.app/docs/runtimes/android/layouts
Control how graphics are laid out within the canvas.
## The Fit Mode
A Rive graphic authored in the editor will not necessarily match the size of the container it is rendered into at runtime. We need to determine the behavior for this scenario, as no one size fits all.
The solution is choosing the fit mode. This is specified on the container and controls how Rive is scaled.
* `Layout`: Use the Rive layout engine to apply responsive layout to the artboard, matching the container dimensions. For this to work, the artboard must be designed with layouts in mind. See [Responsive Layouts](#responsive-layouts) for more information on how to use this option.
* `Contain`: **(Default)** Preserve aspect ratio and scale the artboard so that its larger dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will leave space on the shorter dimension's axis.
* `ScaleDown`: Preserve aspect ratio and behave like `Contain` when the artboard is larger than the container. Otherwise, use the artboard's original dimensions.
* `Cover`: Preserve aspect ratio and scale the artboard so that its smaller dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will clip the artboard on the larger dimension's axis.
* `FitWidth`: Preserve aspect ratio and scale the artboard width to match the container's width.
If the aspect ratios between the artboard and container do not match, this will result in either vertical clipping or space in the vertical axis.
* `FitHeight`: Preserve aspect ratio and scale the artboard height to match the container's height.
If the aspect ratios between the artboard and container do not match, this will result in either horizontal clipping or space in the horizontal axis.
* `Fill`: Do not preserve aspect ratio and stretch to the container's dimensions.
* `None`: Do not scale. Use the artboard's original dimensions.
For either dimension, if the artboard's dimension is larger, it will be clipped. If it is smaller, it will leave space.
### Alignment
In all options other than `Layout` and `Fill`, there is the possibility that the Rive graphic is clipped or leaves space within its container. Alignment determines how content aligns within the container. The following options are available.
* `TopLeft`
* `TopCenter`
* `TopRight`
* `CenterLeft`
* `Center` **(Default)**
* `CenterRight`
* `BottomLeft`
* `BottomCenter`
* `BottomRight`
### Applying the Fit Mode
The `Fit` sealed class is used to specify the fit mode. On all variants other than `Layout` and `Fill`, the `Alignment` enum can be supplied to specify the alignment. Note that because this is a class, it must be constructed. If an alignment is not provided, it defaults to `Alignment.Center`.
```kotlin theme={null}
Rive(
myRiveFile,
fit = Fit.Cover(Alignment.TopCenter)
)
```
The `Fit` enum specifies the fit mode with the follow options: `LAYOUT`, `CONTAIN`, `SCALE_DOWN`, `COVER`, `FIT_WIDTH`, `FIT_HEIGHT`, `FILL`, and `NONE`.
The `Alignment` enum specifies the alignment with the following options: `TOP_LEFT`, `TOP_CENTER`, `TOP_RIGHT`, `CENTER_LEFT`, `CENTER`, `CENTER_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_CENTER`, and `BOTTOM_RIGHT`.
### Using XML Layouts
The fit and alignment enum values can be applied to the `riveFit` and `riveAlignment` attributes in your XML layout:
```xml theme={null}
```
### Using Kotlin
The fit and alignment enum values can be applied to the `fit` and `alignment` properties on your `RiveAnimationView` instance:
```kotlin theme={null}
animationView.fit = Fit.FILL
animationView.alignment = Alignment.CENTER
```
## Responsive Layouts
Rive’s layout feature lets you design resizable artboards with built-in responsive behavior, configured from the editor. Ensure the fit mode is set to **Layout** at runtime and the artboard will resize to fill its container according to the constraints defined in the editor.
Optionally you may provide a **layout scale factor** to multiply the scale of the content. This allows fine tuning the visual size within your container. This property only applies when setting the **Fit** mode to **Layout**.
For more Editor information and how to configure your graphic, see [Layouts Overview](/docs/editor/layouts/layouts-overview).
See also the [Compose Layout](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/ComposeLayoutActivity.kt) sample.
Set the `fit` parameter to `Fit.Layout` in the `Rive` composable. This will resize the artboard to match the container size. The `Fit.Layout` constructor takes an optional `layoutScaleFactor` parameter to adjust the scale of the artboard. By default it is 1, i.e. no scaling.
```kotlin theme={null}
Rive(
myRiveFile,
fit = Fit.Layout(1.2f) // 1.2x scale
)
```
### Sample
See the [Layout](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/LayoutActivity.kt) sample.
### Using XML Layouts
Set the `riveFit` attribute to `"LAYOUT"`.
```kotlin theme={null}
```
### Using Kotlin
Set the RiveAnimationView's `fit` property to `LAYOUT`.
```kotlin theme={null}
val animationView = findViewById(R.id.my_view)
animationView.fit = Fit.LAYOUT
```
### Adjusting the Layout Scale Factor
To adjust the scale factor of the contents, use the `layoutScaleFactor` property. This is nullable, so by default, it will use the density as reported by `resources.displayMetrics.density`. You can override this to any positive float value, or return control to the system by resetting to `null`:
```kotlin theme={null}
// Force a set scale factor
animationView.layoutScaleFactor = 2.5f
// Reset to system control
animationView.layoutScaleFactor = null
```
### Resizing the Artboard
The artboard size can be manually controlled by using the `width` and `height` properties. `resetArtboardSize()` can be used to return these values to their defaults.
```kotlin theme={null}
// Force a certain artboard size
animationView.controller.activeArtboard?.width = 1000f
animationView.controller.activeArtboard?.height = 1000f
// Reset the artboard size to defaults
animationView.controller.activeArtboard?.resetArtboardSize()
```
# Getting Started (Legacy API)
Source: https://rive.app/docs/runtimes/android/legacy-getting-started
Getting started instructions for the Rive Android Legacy API.
## Adding Rive to Your Project
See the [Adding Rive to Your Project](/docs/runtimes/android/android#adding-rive-to-your-project) section for instructions on adding Rive to your Android project. Once complete, resume here.
## Building a RiveAnimationView
There are a number of ways to add Rive animations to your Android application.
Before getting started, ensure your Rive files (.riv) are included in your Android project. The recommended way is to add them to the raw resources (`res/raw`) folder of your project.
### Using setRiveResource or setRiveUrl
For the simplest programmatic initialization, use `setRiveResource` (local) or `setRiveUrl` (networked) methods. They have a number of optional parameters to customize the view.
```kotlin theme={null}
class MyActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val riveView = RiveAnimationView(this)
riveView.setRiveResource(R.raw.my_rive_file)
// or
riveView.setRiveUrl("https://mycdn.myorg.com/my_rive_file.riv")
setContentView(riveView)
}
}
```
### Using RiveAnimationView\.Builder
Rive also provides a builder pattern for constructing a `RiveAnimationView` which allows for staggered initialization steps. Note that the `setResource` method can take a raw resource ID, a URL (string), a byte array, or a Rive `File`.
```kotlin theme={null}
val riveView = RiveAnimationView.Builder(this)
.setResource(R.raw.my_rive_file)
// or
.setResource("https://mycdn.myorg.com/my_rive_file.riv")
.build()
setContentView(riveView)
```
### Using a Rive File
If you have already loaded a Rive `File` instance, you can use that to initialize the view as well. See [Caching a Rive File](/docs/runtimes/caching-a-rive-file) for more details on how and why to load Rive files.
```kotlin theme={null}
// Loads bytes on the main thread for simplicity; consider loading on a background thread for production use.
val bytes = resources.openRawResource(R.raw.rating).use { res -> res.readBytes() }
val riveFile = File(bytes)
val riveView = RiveAnimationView(this)
riveView.setRiveFile(riveFile)
// Release the file if you no longer need it, keep if you plan to reuse it.
riveFile.release()
setContentView(riveView)
```
### Using Compose (AndroidView)
You can also use `RiveAnimationView` inside a Compose UI using the `AndroidView` composable. See also the [LegacyComposeActivity](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/LegacyComposeActivity.kt) in the example app.
```kotlin theme={null}
setContent {
AndroidView(
factory = { context ->
RiveAnimationView(context).also {
it.setRiveResource(R.raw.my_rive_file)
}
}
)
}
```
### Using XML Layouts
To use XML, include it as part of your layout. It has a number of optional attributes to customize the view.
```xml theme={null}
```
If you would rather load the Rive file from a hosted location, use the `app:riveUrl` attribute. Ensure you have the necessary [internet permissions](#internet-permissions).
```xml theme={null}
```
From your activity you can load it as usual:
```kotlin theme={null}
setContentView(R.layout.my_layout)
```
## Internet Permissions
If you're retrieving Rive files over a network, your app will need permission to access the internet in `AndroidManifest.xml`:
```xml theme={null}
```
Note that this isn't necessary if you include the .riv files in your Android project and load them as raw resources.
See the pages in the "Runtime Fundamentals" section to learn how to control animation playback, state machines, and more.
# Loading Assets
Source: https://rive.app/docs/runtimes/android/loading-assets
Loading and replacing assets dynamically at runtime
If you want to dynamically replace images, use image data binding.
Some Rive files may contain assets that can be embedded within the actual file binary, such as font, image, or audio files. The Rive runtimes may then load these assets when the Rive file is loaded. While this makes for easy usage of the Rive files/runtimes, there may be opportunities to load these assets in or even replace them at runtime instead of embedding them in the file binary.
There are several benefits to this approach:
* Keep the `.riv` files tiny without potential bloat of larger assets
* Dynamically load an asset for any reason, such as loading an image with a smaller resolution if the `.riv` is running on a mobile device vs. an image of a larger resolution for desktop devices
* Preload assets to have available immediately when displaying your `.riv`
* Use assets already bundled with your application, such as font files
* Sharing the same asset between multiple `.riv`s
## Methods for Loading Assets
There are currently three different ways to load assets for your Rive files.
In the Rive editor select the desired asset from the **Assets** tab, and in the inspector choose the desired export option:
### Embedded Assets
In the Rive editor, static assets can be included in the `.riv` file, by choosing the *"Embedded"* export type. As stated in the beginning of this page, when the Rive file gets loaded, the runtime will implicitly attempt to load in the assets embedded in the `.riv` as well, and you don't need to concern yourself with loading any assets manually.
**Caveat:** Embedded assets may bulk up the file size, especially when it comes to fonts when using Rive Text ([Text Overview](/docs/editor/text/text-overview)).
**Embedded is the default option.**
### Loading via Rive's CDN
In the Rive editor, you can mark an imported asset as a *"Hosted"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, but will be hosted on Rive's CDN. This means that at runtime when loading in the file, the runtime will see the asset is marked as "Hosted" and load the asset in from the Rive CDN, so that you don't need to concern yourself with loading anything yourself, and the file can still remain tiny.
**Caveat:** The app will make an extra call to a Rive CDN to retrieve your asset
Hosted assets are available on Voyager and Enterprise plans. [Learn more about
our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
### Image CDNs
Some image CDNs allow for on-the-fly image transformations, including resizing, cropping, and automatic format conversion based on the browser's and device's capabilities. These CDNs can host your Rive image assets. Note that for these CDNs, you may need to specify the accepted formats, for example, as part of the HTTP header request:
```html theme={null}
... headers: { Accept: 'image/png,image/webp,image/jpeg,*/*', } ...
```
Please see your CDN provider's documentation for additional information.
Rive supports the following image formats: **jpeg**, **png**, and **webp**
### Referenced Assets
In the Rive editor, you can mark an imported asset as a *"Referenced"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, and the responsibility of loading the asset will be handled by your application at runtime.
This option enables you to dynamically load in assets via a handler API when the runtime begins loading in the `.riv` file. This option is preferable if you have a need to dynamically load in a specific asset based on any kind of app/game logic, and especially if you want to keep the .riv file size small.
All referenced assets, including the `.riv`, will be bundled as a zip file when you export your animation.
**Caveat:** You will need to provide an asset handler API when loading in Rive which should do the work of loading in an asset yourself. See [Handling Assets](#handling-assets).
SVG assets can't currently be loaded at runtime as referenced assets.
This is because SVGs are converted to Rive vector objects, which are always embedded into the .riv.
## Handling Assets
### Examples
* [https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/AssetLoaderFragment.kt](https://github.com/rive-app/rive-android/blob/master/app/src/main/java/app/rive/runtime/example/AssetLoaderFragment.kt)
### Using the Asset Handler API
When instantiating a new `RiveAnimationView`, set a new attribute called `riveAssetLoaderClass` whose value is a string name of the full path to a class that will be responsible for either handling the load of an asset at runtime or passing on the responsibility and giving the runtime a chance to load it otherwise.
**via XML**
```kotlin theme={null}
```
In your accompanying activity, create a new class with the name provided to `riveAssetLoaderClass`, who should implement the `ContextAssetLoader` abstract class from the Rive runtime. Here, you can override a `loadContents` function, which will do the work of determining what assets (if any) to load:
* `asset` - Reference to a `FileAsset` object. You can grab a number of properties from this object, such as the name, asset type, and more. You'll also use this to set a new Rive-specific asset for the dynamically loaded in asset you want to set
* `bytes` - Array of bytes for the asset (if possible, such as if it's an embedded asset)
```kotlin theme={null}
override fun loadContents(asset: FileAsset, inBandBytes: ByteArray): Boolean
```
**Important**: Note that the return value is a `boolean`, which is where you need to return `true` if you intend on handling and loading in an asset yourself, or `false` if you do not want to handle asset loading for that given asset yourself, and attempt to have the runtime try to load the asset.
**Example Usage**
To accompany the XML snippet above, here's an example of what the accompanying activity may look like:
```kotlin theme={null}
package app.rive.runtime.example
import android.content.Context
import android.os.Bundle
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import app.rive.runtime.kotlin.core.ExperimentalAssetLoader
import app.rive.runtime.kotlin.core.FileAsset
import app.rive.runtime.kotlin.core.ContextAssetLoader
import kotlin.random.Random
class FontLoadActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.rive_font_load_simple)
}
}
open class HandleSimpleRiveAsset(context: Context) : ContextAssetLoader(context) {
private val fontPool = arrayOf(
R.raw.montserrat,
R.raw.opensans,
)
/**
* Override this method to customize the asset loading process.
*/
override fun loadContents(asset: FileAsset, inBandBytes: ByteArray): Boolean {
val randFontIndex = Random.nextInt(fontPool.size)
val fontToLoad = fontPool[randFontIndex]
context.resources.openRawResource(fontToLoad).use {
// Load in the font bytes to the asset
return asset.decode(it.readBytes())
}
}
}
```
## Additional Resources
# Logging
Source: https://rive.app/docs/runtimes/android/logging
This Rive runtime includes logging capabilities to help with debugging. These logs are *only* for debugging purposes; nothing is sent over the network, and no personally identifiable information (PII) is logged.
The table below showcases the runtimes that support logging.
The new runtime includes a fine-grained, flexible logging system that allows you to capture logs at various levels (debug, info, warning, error) and redirect them to your preferred logging framework or sink. This is done by implementing the `RiveLog.Logger` interface and assigning the global `RiveLog.logger` property.
The library ships with a default implementation that logs to Android Logcat. Enable it with the following:
```kotlin theme={null}
RiveLog.logger = RiveLog.LogcatLogger()
```
Fine-grained Logging is not supported in the legacy runtime. We may consider retrofitting more logging capabilities to the legacy runtime in the future, especially if doing so would illuminate a potential source for bugs.
# Migrating from the Legacy Rive Android Runtime
Source: https://rive.app/docs/runtimes/android/migrating-from-legacy
A guide to help you transition from the legacy Rive Android runtime to the new runtime.
## Overview
The [new Rive Android runtime](/docs/runtimes/android/android) is a **near-complete rewrite** of both the public API and the internal architecture. While conceptually most operations have an equivalent, the two APIs are incompatible. Any existing work in the legacy API that you would like to migrate must be rebuilt using the new API. This guide aims to demonstrate how to migrate each feature area.
This guide covers:
1. [Shared Features](#shared-features) - Common operations and their equivalents.
2. [New Exclusive Features](#new-exclusive-features) - Features available only in the new runtime.
3. [Legacy Exclusive Features](#legacy-exclusive-features) - Features no longer supported in the new runtime. Most have workarounds or alternatives; these removals simplify the API surface, reduce maintenance overhead, and eliminate anti-patterns.
This guide is not meant to be exhaustive as it would be redundant with existing general documentation. Please refer to the relevant sections of the documentation for more details on specific topics.
## Package
The legacy Rive Android runtime is provided in the `app.rive.runtime.kotlin` package (+ `.core`). The new runtime is provided in the `app.rive` package (+ `.core`). This allows identical class names, such as `Artboard` to coexist without conflict. If you need to use identically named classes from both packages in the same file, you can use Kotlin's `import as` feature to alias one of the imports.
```kotlin theme={null}
import app.rive.Artboard
import app.rive.runtime.kotlin.core.Artboard as LegacyArtboard
```
## Asynchronous APIs
Due to the new threading model based on the `RiveWorker`, almost all operations which return values that were originally synchronous on the main thread are now asynchronous `suspend` functions, e.g. loading Rive files and performing queries. You will need to call these functions from a coroutine context, such as within a `LaunchedEffect` in Compose or using `lifecycleScope.launch` in an Activity or Fragment.
The most common Compose operations are wrapped in helpers, such as `rememberRiveFile`, which expose the async state as a `Result` type for easier consumption in Composables. Other operations are "fire and forget", such as creating artboards and view model instances from a loaded Rive file, or advancing a state machine.
## Lifecycles
The legacy Rive Android runtime is View based, with the `RiveAnimationView` class managing its lifecycle based on the owning Activity or Fragment lifecycle. The Rive file's lifetime is associated with the view's, as well as the renderer, controller, and created objects such as artboards and state machines. These used hierarchical reference counting to manage their lifetimes.
The new runtime makes use of the `AutoCloseable` interface to manage lifetimes explicitly for objects such as the Rive file, artboards, and state machines. The worker by contrast is reference counted and owns all memory for resources it creates. When creating objects manually, you must `close` them to free their resources or they will last for the duration of the worker. Alternatively, you can use them with the `use` helper, which will call `close` at the end of its block. The worker itself must have all references released or it and its resources will leak.
In Compose, lifecycles are tied to `remember` Composables and use `DisposableEffect` to create and dispose of objects as needed, simplifying memory management.
## Shared Features
### Shared APIs
A few APIs are shared between the legacy and new runtimes. They are still in the `app.rive.runtime.kotlin` package of the legacy runtime.
* The `Rive` global object for initialization, including the `Rive.init` method.
* The `RiveInitializer` XML initialization helper.
* The fallback font APIs, including the `FontFallbackStrategy` interface and the `FontFallbackStrategy.stylePicker` global.
These shared APIs may change in the future to be fully separated with revised versions in the new package. This will only happen as a breaking change in a major version release.
### RiveAnimationView to Rive
The legacy Rive Android runtime is View based and provided the `RiveAnimationView` class to display Rive animations. The name reflects the more limited feature set at the time of its creation, being mostly for animation playback.
The new API is Compose based, with the intention to provide a View API in the future. The main Composable is simply called `Rive`, reflecting the broader feature set and use cases it supports.
#### Setting a Local Raw Resource
The new API will use the I/O dispatcher by default to load Rive files asynchronously. This is different from the legacy runtime, which loaded files synchronously on the main thread. If you want to load files using a different threading model, load the resource using your preferred method and pass the bytes to a `RiveFileSource.Bytes` source.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file)
```
**Legacy XML Layout**
```xml theme={null}
```
**New Rive Composable**
```kotlin theme={null}
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // Handle loading and error states appropriately
}
```
#### Setting a Rive File from Bytes
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(myBytes)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveBytes(myBytes)
```
**New Rive Composable**
```kotlin theme={null}
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.Bytes(myBytes),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // Handle loading and error states appropriately
}
```
#### Caching a Rive File
See also Caching a Rive File for more details.
In the new runtime, Rive files are always created explicitly, as opposed to often being created implicitly by the view. This makes it more obvious to cache and reuse Rive files across multiple Rive instances.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Load bytes on the main thread for simplicity; consider loading on a background thread for production use.
val bytes = resources.openRawResource(R.raw.my_rive_file).use { res -> res.readBytes() }
val riveFile = File(bytes)
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(riveFile)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveFile(riveFile)
// Release the file if you no longer need it, keep if you plan to reuse it.
riveFile.release()
```
**New Rive Composable**
The Rive file produced by `rememberRiveFile` can be hoisted to a higher level Composable to live longer and be shared across multiple Rive instances.
```kotlin theme={null}
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> Rive(riveFile.value)
else -> {} // Handle loading and error states appropriately
}
```
#### Choosing an Artboard and State Machine
See also the artboard documentation for more details.
See also the state machine documentation for more details.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setArtboardName("My Artboard")
.setStateMachineName("My State Machine")
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
artboardName = "My Artboard",
stateMachineName = "My State Machine"
)
```
**Legacy XML Layout**
```xml theme={null}
```
**New Rive Composable**
Artboards and state machines are passed as objects rather than names. You can use the `rememberArtboard` and `rememberStateMachine` helpers to get these objects from a loaded `RiveFile`.
```kotlin theme={null}
val riveWorker = rememberRiveWorker()
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Success -> {
val artboard = rememberArtboard(riveFile.value, "My Artboard")
val stateMachine = rememberStateMachine(artboard, "My State Machine")
Rive(
riveFile.value,
artboard = artboard,
stateMachine = stateMachine
)
}
else -> {} // Handle loading and error states appropriately
}
```
#### Setting the Fit and Alignment
See also the Layout documentation for more details.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setFit(Fit.CONTAIN)
.setAlignment(Alignment.CENTER)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file,
fit = Fit.CONTAIN,
alignment = Alignment.CENTER
)
// Property
riveView.controller.fit = Fit.CONTAIN
riveView.controller.alignment = Alignment.CENTER
```
**Legacy XML Layout**
```xml theme={null}
```
**New Rive Composable**
The new runtime models fit modes as a sealed class, allowing alignment only on variants that support it. Additionally, the `Layout` variant exposes the layout scale factor.
```kotlin theme={null}
Rive(
riveFile,
fit = Fit.Contain(Alignment.Center),
)
```
#### Auto Binding
See also the auto binding section in data binding for more details.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoBind(true)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoBind = true)
```
**Legacy XML Layout**
```xml theme={null}
```
**New Rive Composable**
Auto-binding was not implemented in the Compose API due to the nature of composables. Since they are functions, it is difficult to get values out of them as compared to classes. Callbacks would require a null placeholder to remember the value before it has fired which creates more overhead than supplying the instance directly.
The equivalent is to create a view model instance with no source. This will internally create the default artboard, the default view model for that artboard, and the default instance for that view model. You can then pass that into the Rive composable.
```kotlin theme={null}
val vmi = rememberViewModelInstance(riveFile)
Rive(
riveFile,
viewModelInstance = vmi,
)
```
#### Playing, Pausing, and State Machine Settling
**Legacy RiveAnimationView**
The legacy API includes `play()` and `pause()` methods on the `RiveAnimationView` class to control playback of the animation. Notably, the paused state is the same as the settled state, which is not ideal. `play()` will both unpause and unsettle the state machine, causing it to advance.
```kotlin theme={null}
riveView.play() // Unsettles and plays
riveView.pause() // Pauses
```
**New Rive Composable**
In the new runtime, there are no play or pause methods. Instead, control of the play state is done through the `playing` parameter on the `Rive` Composable. Setting `playing` to `false` will pause the animation, while setting it to `true` will resume it. State machine settling and unsettling is done automatically and covers all scenarios that would require it.
```kotlin theme={null}
var isPlaying by remember { mutableStateOf(true) }
Rive(
riveFile,
playing = isPlaying
)
```
#### Autoplay and Setting Initial Values
The legacy runtime includes an `autoplay` feature, defaulting to true, that automatically starts playing the given animation or state machine when the Rive file is loaded. This can be disabled to allow setting initial values before playback begins.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAutoPlay(true)
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setRiveResource(R.raw.my_rive_file, autoPlay = true)
```
**Legacy XML Layout**
```xml theme={null}
```
The new runtime does not include an `autoplay` feature as it is not necessary due to the decoupling of Rive files from their presentation. You can leave `playing` at its default value of `true` to achieve similar behavior. To set initial values before playback begins, you can set `playing` to `false`, update your data-bound properties, and then set `playing` to `true` to start playback.
```kotlin theme={null}
var isPlaying by remember { mutableStateOf(false) }
val vmi = rememberViewModelInstance(riveFile)
LaunchedEffect(vmi) {
// Set initial values
vmi.setNumber("My Number Property", 42.0f)
// Start playback
isPlaying = true
}
Rive(
riveFile,
playing = isPlaying,
viewModelInstance = vmi,
)
```
#### Touch Pass-Through
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setTouchPassThrough(true)
.build()
// Property
riveView.touchPassThrough = true
```
**Legacy XML Layout**
```xml theme={null}
```
**New Rive Composable**
The new runtime provides the `touchPassThrough` parameter on the `Rive` Composable. This is more nuanced, as it can be set to `Consume` (default behavior), `Observe`, or `PassThrough`. `Observe` shares only with ancestors in the Compose hierarchy, useful for not blocking scroll events, while `PassThrough` also shares with siblings (equivalent to the legacy behavior).
```kotlin theme={null}
Rive(
riveFile,
touchPassThrough = RivePointerInputMode.PassThrough,
)
```
### View Models
**Legacy**
The legacy runtime includes a `ViewModel` class retrieved from a `File` which corresponds to a view model in the Rive Editor. This class provides methods to query and create view model instances.
```kotlin theme={null}
val viewModel = riveFile.getViewModelByName("My View Model")
val instance = viewModel.createInstanceFromName("My Instance")
val properties = viewModel.properties
```
**New**
The new runtime collapses querying into the `RiveFile` class, where the view model name is included when required for queries. View model instances are specified by a `ViewModelSource`, which can be either `Named` or `DefaultForArtboard`. This `ViewModelSource` can then be converted into a `ViewModelInstanceSource` using the `blankInstance`, `defaultInstance`, or `namedInstance` helpers. This completed source can then be passed to `rememberViewModelInstance` or `ViewModelInstance.fromFile`.
```kotlin theme={null}
val instanceSource = ViewModelSource.Named("My View Model").namedInstance("My Instance")
val instance = rememberViewModelInstance(riveFile, instanceSource)
LaunchedEffect(riveFile) {
val properties = riveFile.getViewModelProperties("My View Model")
}
```
### View Model Instance Properties
**Legacy**
The legacy runtime provides explicit property objects on the `ViewModelInstance` class for querying and updating data-bound properties.
```kotlin theme={null}
// Getting a property
val numberProperty = vmi.getNumberProperty("My Number Property")
// Reading its value
val numberValue = numberProperty.value
// Observing its value
numberProperty.valueFlow.collect { value ->
// Handle value updates
}
// Writing its value
numberProperty.value = 42.0f
```
**New**
The new runtime does not provide explicit property objects. Instead, properties are queried and updated on the `ViewModelInstance` by name using getter and setter methods.
```kotlin theme={null}
LaunchedEffect(vmi) {
// Reading a property value
val numberValue = vmi.getNumberFlow("My Number Property").first()
// Observing its value
vmi.getNumberFlow("My Number Property").collect { value ->
// Handle value updates
}
}
// Observing in Compose
val numberValue by vmi.collectNumberAsState("My Number Property", initial = 0.0f)
// Writing a property value
vmi.setNumber("My Number Property", 42.0f)
```
### Bindable Artboards
**Legacy**
The legacy runtime includes a `BindableArtboard` class that wraps a native `Artboard` and provides data-binding capabilities. This class is meant to limit the API surface and prevent anti-patterns by restricting direct access to the underlying artboard.
```kotlin theme={null}
val bindableArtboard = riveFile.createBindableArtboardByName("My Artboard")
val artboardProperty = vmi.getArtboardProperty("My Artboard Property")
artboardProperty.set(bindableArtboard)
```
**New**
The new runtime does not include a `BindableArtboard` class. Instead, the same `Artboard` class is used in both cases.
```kotlin theme={null}
val artboard = rememberArtboard(riveFile, "My Artboard")
vmi.setArtboard("My Artboard Property", artboard)
```
### Loading Referenced Assets
**Legacy**
The legacy runtime uses a "pull" model for loading referenced assets, where the runtime requests assets by callback as needed through the `FileAssetLoader` class or its subclasses. This allows lazy loading of assets, but removes control from the user for when assets are loaded.
**Legacy RiveAnimationView**
```kotlin theme={null}
class MyAssetLoader(private val context: Context) : ContextAssetLoader(context) {
// Example asset loader that handles one image asset named "my_image"
override fun loadContents(asset: FileAsset, inBandBytes: ByteArray): Boolean {
val identifier =
if (asset.uniqueFilename == "my_image") R.raw.my_image
else return false
context.resources.openRawResource(identifier).use {
asset.decode(it.readBytes())
}
return true
}
}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAssetLoader(MyAssetLoader(context))
.build()
// Set method
val riveView = RiveAnimationView(context)
riveView.setAssetLoader(MyAssetLoader(context))
riveView.setRiveResource(R.raw.my_rive_file)
```
**Legacy XML Layout**
```xml theme={null}
```
**New**
The new runtime uses a "push" model, where the application is responsible for supplying referenced assets to the worker before they are needed. If assets are supplied after they are required, they will "pop in" instead. This change was made to simplify the internal architecture and to allow users the flexibility to load assets in whatever manner they choose.
The current way to retrieve the list of required assets is through inspecting the files in the exported .zip. In the future we will add an API to query the manifest of required assets from a Rive file.
Assets are "global" to the worker, meaning they can be shared across multiple Rive files and instances, assuming they have the same key. This reduces memory usage for common assets such as fonts.
```kotlin theme={null}
val riveWorker = rememberRiveWorker()
val bytesResult = produceState>(Result.Loading) {
value = withContext(Dispatchers.IO) {
context.resources.openRawResource(R.raw.my_image)
.use { Result.Success(it.readBytes()) }
}
}.value
val image = bytesResult.andThen { bytes -> rememberRegisteredImage(riveWorker, "My Image", bytes) }
```
### Default Layout Scale Factor
The legacy runtime uses the reported device density as the default scale factor when rendering Rive with Layout as the fit mode.
The new runtime uses a default scale factor of 1.0, meaning Rive units map directly to screen pixels. In practice, matching density often results in visuals appearing too large. However, if that is the desired behavior and you want to match the legacy runtime, you can set the scale factor to the device density using `LocalDensity`.
```kotlin theme={null}
val density = LocalDensity.current.density
Rive(
riveFile,
fit = Fit.Layout(scaleFactor = density)
)
```
## New Exclusive Features
A growing number of features are only available in the new runtime. These features were added to address limitations in the legacy runtime or to provide new capabilities that were not previously possible. Below is a list of such features along with brief descriptions.
### Logging
The new runtime includes a fine-grained, flexible [Logging](/docs/runtimes/android/logging) system that allows you to capture logs at various levels (debug, info, warning, error) and redirect them to your preferred logging framework or sink.
By comparison, the legacy runtime has limited logs.
We may consider retrofitting more logging capabilities to the legacy runtime in the future, especially if doing so would illuminate a potential source for bugs.
### Tracking the Loading State of a Rive File
The new runtime provides a `Result` wrapper around Rive file loading operations, allowing you to track the state (loading, success, error) of a Rive file. This is useful for providing user feedback during loading or handling errors gracefully.
```kotlin theme={null}
val riveFile = rememberRiveFile(
RiveFileSource.RawRes.from(R.raw.my_rive_file),
riveWorker
)
when (riveFile) {
is Result.Loading -> {
// Show loading indicator
}
is Result.Failure -> {
// Show error message
}
is Result.Success -> {
Rive(riveFile.value)
}
}
```
The legacy runtime's convenience methods, e.g. `setRiveResource()`, do not provide a way to track loading state or handle errors directly. Instead, the file must first be loaded using the `File` class. Loading is normally synchronous, though could be performed on a background thread. With some effort similar loading state tracking could be implemented.
There are no plans to retrofit this feature into the legacy runtime.
### Rendering to a Bitmap
The new runtime provides built-in support for [rendering Rive to an Android `Bitmap`](/docs/runtimes/advanced-topic/rendering-to-a-bitmap), which can be useful for scenarios such as snapshot testing or rendering video encoded from data-bound Rive files into image frames on the edge (i.e. the user's device). This is done using the `RenderBuffer` class or the `onBitmapAvailable` callback in the `Rive` Composable.
Rendering to a Bitmap is technically possible in the legacy runtime by rendering to a Canvas backed by a Bitmap or by sub-classing the renderer, but it is not a built-in feature and requires more effort to implement.
There are no plans to retrofit this feature into the legacy runtime.
### Updating a Data Bind Unsettles the State Machine
In the new runtime, updating a data-bound property [automatically "unsettles" the state machine](/docs/runtimes/android/android#adding-rive-to-your-composition), causing it to advance and draw based on the new data. By comparison, the legacy runtime required you to call `RiveAnimationView.play()` after updating data-bound properties on a settled state machine to achieve the same effect.
We may consider retrofitting this behavior into the legacy runtime in the future as it is a common source of confusion. However, doing so would be a breaking change, and it may not be worth the disruption for existing users.
## Legacy Exclusive Features
Some features are only available in the legacy runtime. This is usually because they were less commonly used, were complex in practice, or were difficult to maintain and support. Below is a list of such features along with guidance on how to handle their absence in the new runtime.
### View-Based API
The legacy runtime is built around the `RiveAnimationView` class, which is a subclass of Android's `TextureView` (and subsequently `View`). This makes it easy to integrate into existing View-based Android applications. The new runtime is currently Compose-based and does not provide a View subclass. If you need to use Rive in a View-based application, you will need to set up a Compose environment within your View hierarchy using `ComposeView`.
There are plans to provide a View-based API in the future.
### Frame Metrics
The legacy runtime includes support for frame metrics, providing some rendering performance numbers and potentially helping to identify bottlenecks. This is done by enabling frame metrics collection on the `RiveAnimationView`.
```kotlin theme={null}
riveView.startFrameMetrics()
```
The new runtime does not yet include any comparable feature. Instead, you can use Android's built-in profiling tools, such as the Android Profiler in Android Studio, to measure rendering performance and identify bottlenecks.
We will be adding performance monitoring in the future. The specific form it takes is to be determined.
### CDN Assets
**Legacy**
The Rive Editor allows marking an asset as "Hosted", meaning it is uploaded to Rive's CDN. The legacy runtime includes built-in support for loading these assets directly from the CDN when the Rive file is loaded. This is done automatically and by default when using the `RiveAnimationView` class.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API (enabled by default)
val riveView = RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setShouldLoadCDNAssets(true)
.build()
// Using asset loader (enabled by default)
val riveView = RiveAnimationView(context)
riveView.setAssetLoader(FallbackAssetLoader(context, loadCDNAssets = true))
```
**Legacy XML Layout**
```xml theme={null}
```
**New**
The new runtime does not include built-in support for loading CDN assets.
We will be adding a new API in a future update to query the asset manifest for a file, which will include URLs for hosted assets. This will allow you to load them using standard networking libraries and supply them to the Rive worker using existing asset APIs.
### Setting a Rive File from Network
The legacy runtime provides built-in support for loading Rive files from network URLs using the now defunct Volley library. This includes methods such as:
**Legacy Builder API**
```kotlin theme={null}
RiveAnimationView.Builder(context)
.setResource("https://example.com/animation.riv")
```
**Legacy XML Layout**
```xml theme={null}
```
The new runtime does not include this feature directly. Instead you can implement it using standard Android networking libraries (e.g., Retrofit, OkHttp) to fetch the file and then load it using a `RiveFileSource.Bytes` source, passing the downloaded bytes. This source can be supplied to `rememberRiveFile` when using Compose or `RiveFile.fromSource` when working outside of a Compose context.
We are considering adding built-in network loading capabilities in future releases. If implemented, it would not use Volley, preferring a modern alternative. It would also likely be opt-in to avoid adding unnecessary dependencies for users who do not need network loading.
### Rendering to Canvas
See also Choose a Renderer for more details.
The legacy runtime includes a Canvas renderer that allows rendering Rive animations directly into an Android `Canvas`. This was useful as a fallback for scenarios where the Rive Renderer wasn't yet feature compatible. As we have improved the Rive Renderer, this use case has diminished.
The Canvas renderer is enabled with the following APIs.
**Rive Global Default**
```kotlin theme={null}
Rive.defaultRendererType = RendererType.Canvas
```
**Legacy RiveAnimationView Builder**
```kotlin theme={null}
RiveAnimationView.Builder(context)
.setRendererType(RendererType.Canvas)
```
**Legacy XML Layout**
```xml theme={null}
```
The new runtime does not include a Canvas renderer, focusing instead on the Rive Renderer for optimal performance and feature support, such as vector feathering. Additionally, the Canvas renderer has limitations around performance and visual fidelity compared to the Rive Renderer.
There are no current plans to reintroduce Canvas rendering in the new runtime.
### Artboard Volume
The legacy runtime can set an artboard's audio volume through the `Artboard.volume` property or `RiveAnimationView.setVolume()`. This feature is not present in the new runtime.
We may consider adding audio features in the future, but there are no immediate plans to do so.
### State Machine Inputs
See also the Inputs documentation for more details.
The legacy runtime provides methods to set state machine inputs.
```kotlin theme={null}
riveView.setNumberState("My State Machine", "My Number Input", 42.0f)
riveView.setBooleanState("My State Machine", "My Boolean Input", true)
riveView.fireState("My State Machine", "My Trigger Input")
```
The new runtime does not include these methods. State machine inputs are deprecated in favor of data binding, which provides a more flexible and powerful way to manage changes to state machines.
The Rive Editor provides an automatic conversion tool in Menu > "Convert Inputs to ViewModels". This is a 1:1 conversion that preserves existing behavior while enabling the benefits of data binding, though this may not be ideal for performance or semantics. You can use this feature to perform initial migration when adopting the new runtime and then iterate on the data contract to improve it further.
### Events
See also the Rive Events documentation for more details.
The legacy runtime includes support for listening to Rive events to respond to events emitted from the Rive file. This is done through the `RiveFileController.Listener` interface.
```kotlin theme={null}
riveView.addEventListener(object : RiveFileController.RiveEventListener {
override fun notifyEvent(event: RiveEvent) {
// Handle event
}
})
```
The new runtime does not include this feature. Events are deprecated in favor of data binding, which provides a more flexible and powerful way to listen for changes from the Rive file. Simple events can be replaced with trigger properties in a view model.
There are no current plans to reintroduce event listening in the new runtime.
### Text Runs
See also the Text documentation for more details.
The legacy runtime includes support for text runs, used to get and set dynamic text in Rive content.
```kotlin theme={null}
val text = riveView.getTextRunValue("My Text Run")
riveView.setTextRunValue("My Text Run", "Hello, World!")
```
The new runtime does not include this feature. Text runs are deprecated in favor of data binding, which provides a more flexible and powerful way to manage dynamic text content through string properties. Any text run which was previously addressed by name must instead be bound to a string property in a view model.
```kotlin theme={null}
val vmi = rememberViewModelInstance(riveFile)
// Collect from flow
val text by vmi.getStringFlow("My Text Property").collectAsState(initial = "")
// Or get imperatively
LaunchedEffect(vmi) {
val text = vmi.getStringFlow("My Text Property").first()
}
vmi.setString("My Text Property", "Hello, World!")
```
There are no current plans to reintroduce text run support in the new runtime.
### Linear Animations
See also the Linear Animation documentation for more details.
In the Rive Editor's [animate mode](/docs/editor/fundamentals/design-vs-animate-mode), designers can create both state machines and linear animations. The legacy Android runtime supports playing multiple linear animations directly allowing developers to trigger these animations without using state machines. These included loop modes, direction, duration, and seeking.
**Legacy Kotlin Methods**
```kotlin theme={null}
// Query the list of linear animation names
val linearAnimations = riveView.animations
// Query the playing animations
val playingAnimations = riveView.playingAnimations
// Play a linear animation from the Builder API
RiveAnimationView.Builder(context)
.setResource(R.raw.my_rive_file)
.setAnimationName("My Animation")
// Play a linear animation using the set method
riveView.setRiveResource(R.raw.my_rive_file, animationName = "My Animation")
// Play a linear animation directly
riveView.play("My Animation")
```
**Legacy XML Layout**
```xml theme={null}
```
In practice, working with linear animations is more granular and brittle compared to state machines, and as such they are not directly supported in the new runtime. To achieve similar functionality, you can create a state machine in the Rive Editor that plays the desired linear animation and then trigger that state machine from your Android code.
There are no current plans to reintroduce direct linear animation playback in the new runtime.
### Multiple State Machines Per View
The legacy runtime allows multiple state machines to be associated with a single `RiveAnimationView` instance. At the core runtime level there is no technical limitation, and each state machine is processed in sequence. In practice this leads to complexity and difficulty reasoning about state, especially when data binding is involved. Only the Android legacy runtime supported this capability among the various Rive runtimes.
The new runtime enforces a one-to-one relationship between Rive instances and state machines to simplify state management. If you need to manage concurrent, overlapping states, consider using [state machine layers](/docs/editor/state-machine/layers).
There are no current plans to reintroduce multiple state machines per Rive instance in the new runtime.
### Observing State Machine State
The legacy runtime provides methods to observe which state is currently active in a state machine. This can be useful for triggering actions in your application based on the current state of the animation.
```kotlin theme={null}
riveView.registerListener(object : RiveFileController.Listener {
override fun notifyStateChanged(stateMachineName: String, stateName: String) {
// Handle state change
}
})
```
The new runtime does not include this feature. The original feature was added before data binding and is brittle to Rive file changes. The modern equivalent is through data binding properties emitting values when the state changes. This means looser coupling to the Rive file and allows designers to iterate without breaking the application code.
There are no current plans to reintroduce state observation in the new runtime.
### Single Touch Support
The legacy runtime supports single touch input, allowing only one touch interaction at a time. This is to maintain backwards compatibility with existing Rive files that were designed with single touch in mind. As such it is also the default behavior.
**Legacy RiveAnimationView**
```kotlin theme={null}
// Builder API
val riveView = RiveAnimationView.Builder(context)
.setMultiTouchEnabled(false)
.build()
// Property
riveView.multiTouchEnabled = false
```
**Legacy XML Layout**
```xml theme={null}
```
The new runtime supports only multitouch input. In practice, Rive files should rarely need to distinguish between single and multitouch, as touch points are typically used to trigger interactions rather than being tracked individually. Test your Rive files when migrating to ensure they behave as expected with multitouch input, and adjust the design if necessary.
There are no current plans to reintroduce single touch support in the new runtime.
### Getting by Index
The legacy runtime provides methods to get artboards, animations state machines, view models, and view model instances by their index in addition to their name. This is rarely useful in scenarios where you want to iterate over all elements.
```kotlin theme={null}
val artboard = riveFile.artboard(0)
val animation = artboard.animation(0)
val stateMachine = artboard.stateMachine(0)
val viewModel = riveFile.getViewModelByIndex(0)
val instance = viewModel.createInstanceFromIndex(0)
```
The new runtime can achieve similar functionality by iterating over queried names. For artboards, for example:
```kotlin theme={null}
LaunchedEffect(riveFile) {
val artboardNames = riveFile.getArtboardNames()
for (name in artboardNames) {
Artboard.fromFile(riveFile, name).use {
// Use artboard
}
}
}
```
There are no current plans to reintroduce getting by index in the new runtime.
### Renderer Class
The legacy runtime includes the `Renderer` and `RiveArtboardRenderer` classes that allow you to override rendering behavior for Rive. It is difficult to use correctly, ensure proper life cycles, and maintain compatibility with the Rive Renderer as it evolves.
The new runtime does not include a `Renderer` class, focusing instead on the most common use cases. Other use cases, such as rendering to an Android Bitmap, are supported through dedicated APIs. Render loops are managed by the `Rive` Composable and execute on the `RiveWorker`.
There are no current plans to reintroduce a Renderer class in the new runtime.
### Controller Class
The legacy runtime includes a `RiveFileController` class that provides methods to control playback, manage state machines, and observe state changes. It is tightly coupled to the `RiveAnimationView`.
The new runtime does not need a separate controller class, as control is provided directly through the `Rive` Composable and associated helpers.
There are no current plans to reintroduce a Controller class in the new runtime.
# Playing Audio
Source: https://rive.app/docs/runtimes/android/playing-audio
Playing Rive audio events
To learn more on how to add audio to your Rive file, see [Audio Events](/docs/editor/events/audio-events).
## Embedded Assets
Embedded assets require no additional work to play audio.
## Referenced Assets
Referenced assets require a little bit more work to play audio. Audio will still automatically play, but the audio file(s) must be loaded when a Rive runtime attempts to play audio.
For more information, see [Loading Assets](/docs/runtimes/android/loading-assets).
# Rendering to a Bitmap
Source: https://rive.app/docs/runtimes/android/rendering-to-a-bitmap
Render screenshots and video at runtime.
Rendering to a bitmap at runtime is useful for scenarios such as snapshot testing and rendering video encoded from data-bound files.
For rendering to a bitmap from the Rive Editor, see [Exporting for Video or Static Design](/docs/editor/exporting/exporting-for-video-and-static-design).
See the `RiveSnapshotActivity` example for a demonstration of this feature.
Rendering to a bitmap is done using the `RenderBuffer` class or the `onBitmapAvailable` callback in the `Rive` Composable.
Rendering to a bitmap was technically possible in the legacy runtime by rendering to a Canvas backed by a bitmap or by sub-classing the renderer, but it was not a built-in feature and required more effort to implement. There are no plans to retrofit this feature into the legacy runtime.
# State Machine Playback
Source: https://rive.app/docs/runtimes/android/state-machines
Playing a state machine
For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/docs/editor/state-machine).
A Rive state machine is a set of animation states and the transitions between them. At runtime there is limited ability to observe or modify the state directly. This is by design, as this would limit the ability of a designer in Rive to modify the state machine without creating breaking changes. Instead, state machines are indirectly controlled through transitions conditioned on Data Binding properties.
A designer assigns a default state machine for each artboard in the Rive editor. They may create multiple state machines, each representing a different configuration of states and transitions. When rendering a Rive file and artboard, you may choose which state machine to play. If no state machine is specified, the default state machine for that artboard is used.
## Controlling Playback
State machines play by "advancing" over time. This is done once per frame by the amount of time between frames. For example, for a graphic running at 60 frames per second, the state machine would be advanced by approximately 16.67 milliseconds (1/60th of a second) each frame. This advancing evaluates keyframes, transitions, data bindings changes, and ultimately the visible artboard elements to create the illusion of motion over time.
This runtime provides a way to control whether the state machine is playing. When paused or stopped, the state machine does not advance and the last rendered frame remains visible. When playing from pause, the state machine resumes from where it left off, whereas when playing from stop, it restarts from the entry state.
In addition to the paused/stopped state, state machines may also "settle". This is an optimization where the Rive runtime detects that no further changes will occur (for example, if there are no active transitions or animations). While settled the state machine will also stop advancing. This improves performance and energy use by avoiding unnecessary calculations. State machines are unsettled by external actions that change their state, such as user input or data binding changes. You can additionally force a state machine to unsettle by calling play, though it may immediately re-settle if there is no further work to be done.
## Playing State Machines
By default, the `Rive` composable will select and create the default state machine specified in the Rive editor. To specify a different state machine, you must first create a `StateMachine` object from an `Artboard`.
### Compose
State machine objects can be created in Compose using the `rememberStateMachine` function, which takes an artboard and name of the state machine.
```kotlin theme={null}
val artboard = rememberArtboard(myRiveFile, "My Artboard")
val stateMachine = rememberStateMachine(artboard, "My State Machine")
```
### Outside of Compose
Alternatively, you can create a state machine outside of Compose contexts. Note that with this approach you are responsible for managing the state machine's lifecycle and must eventually close it with `StateMachine::close()` when no longer needed, or leveraging its [`AutoClosable`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-auto-closeable.html) interface with a `use` block.
```kotlin theme={null}
val artboard = Artboard.fromFile(myRiveFile, "My Artboard")
val stateMachine = StateMachine.fromArtboard(artboard, "My State Machine")
...
stateMachine.close()
artboard.close()
```
### Using the State Machine
Once you have a state machine, you can pass it to the `Rive` composable via the `stateMachine` parameter.
By default the Rive composable will play the selected state machine. To toggle the play/pause state, set the `playing` parameter, triggering a re-composition. This is also used to replicate the auto-play behavior of other runtimes - simply set `playing` to false if you need to perform initial setup and set to true once ready.
```kotlin theme={null}
Rive(
myRiveFile,
artboard = artboard,
stateMachine = stateMachine,
playing = true // Or false to pause
)
```
By default RiveAnimationView will auto-play the selected state machine.
⚠️ The legacy API allows for playing multiple linear animations and state machines simultaneously. This is discouraged due to complexity and divergence from other runtimes. Prefer to use a single state machine controlling all animation through states and transitions.
### Using XML Layouts
```xml theme={null}
```
### Using Kotlin
```kotlin theme={null}
animationView.setRiveResource(
R.raw.my_rive_file,
stateMachineName = "My State Machine",
autoplay = true
)
```
### Controlling Playback
You can control the state machine's playing state using the same APIs as those for linear animation playback (i.e. `play`, `pause`, and `stop`). When doing so, ensure you set the `isStateMachine` parameter to `true`.
```kotlin theme={null}
animationView.play("My State Machine", isStateMachine = true)
animationView.pause("My State Machine", isStateMachine = true)
animationView.stop("My State Machine", isStateMachine = true)
```
# Apple
Source: https://rive.app/docs/runtimes/apple/apple
Apple runtime for Rive.
Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.
For more details, refer to the [feature support](/docs/feature-support/) and [choosing a renderer](/docs/runtimes/choose-a-renderer/) pages.
## Overview
This guide documents how to get started using the Apple runtime library. Rive runtime libraries are open-source. The source is available in its [GitHub repository](https://github.com/rive-app/rive-ios).
This library contains an API for Apple apps to easily integrate their Rive assets for both UIKit/AppKit and SwiftUI. The runtime can be installed via Cocoapods or Swift Package Manager.
The Apple runtime currently supports iOS 14.0+, visionOS 1.0+, tvOS 16.0+, macOS 13.1+, and Mac Catalyst 14.0+
The new Apple runtime is designed as a Swift-first API leveraging Swift Concurrency, with improved multi-threading support for Rive.
The entry point is the `Rive` type, which is a container for the configuration of a Rive view. This includes the file, artboard, state machine, fit, and background color.
Adding a Rive view is done by creating a `RiveUIView` with a `Rive` object, and then adding it to the view hierarchy. In SwiftUI, this is as easy as calling `.view()` on a `RiveUIView` object.
It is important to note that while multi-threading is supported, the calls from Rive objects must still be made on the main thread. This is enforced at compile time by marking functions and types as `@MainActor`.
Most APIs are marked as `throws`, with `Error` types available for the different Rive primitives.
## Getting Started
Follow the steps below for a quick start on integrating Rive into your Apple app.
With CocoaPods going into [maintenance mode](https://blog.cocoapods.org/CocoaPods-Support-Plans/), we recommend using Swift Package Manager.
To install via Xcode, you can follow Apple's instructions for [adding a package dependency to your app](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app), with the Apple runtime's GitHub URL: [https://github.com/rive-app/rive-ios](https://github.com/rive-app/rive-ios).
Alternatively, you can add the dependency manually by adding the following to your `Package.swift` file:
```swift theme={null}
dependencies: [
.package(url: "https://github.com/rive-app/rive-ios", from: "6.13.0")
]
```
Then add the dependency to your target:
```swift theme={null}
targets: [
.target(
name: "MyApp",
dependencies: [
.product(name: "RiveRuntime", package: "rive-ios")
]
)
]
```
The new Apple runtime is available in the same Swift package and CocoaPods pod as the legacy runtime. Both runtime APIs are available in the same package, so you can import the runtime using the same import statement.
```swift theme={null}
import RiveRuntime
```
A `Worker` is what handles concurrency in the Rive runtime. This type handles starting a background thread for processing, in addition to handling global (out-of-band) assets.
A `Worker` must be alive for the duration of Rive usage. A `File` creates a strong reference to a `Worker`, so a `Worker` will at least be alive for the duration of use of a `File`, unless a reference to a `Worker` is kept outside of a file.
```swift theme={null}
// In an async context
let worker = try await Worker()
```
For more information on threading, see [Threading](#threading).
Once you have created a `Worker`, you can move onto creating a `File`. Each `File` object takes a source and a worker.
The `File` initializer is marked `@MainActor`.
```swift Local File theme={null}
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
```
```swift Remote URL theme={null}
let worker = try await Worker()
let url: URL = URL(string: "https://example.com/my_file.riv")!
let file = try await File(source: .url(url), worker: worker)
```
```swift Data theme={null}
let worker = try await Worker()
let data: Data = ...
let file = try await File(source: .data(data), worker: worker)
```
Once you have created a `File`, you can move onto creating a `Rive` object. This object defines the configuration of a view. The most basic implementation is created with just a file; Rive will handle loading the default artboard and state machine for rendering. Additionally, if the artboard contains a default view model instance, it will be bound to the state machine automatically.
Once you have created a `Rive` object, you can initialize your view. In UIKit, this is done by creating a `RiveUIView` and adding it to the view hierarchy. In SwiftUI, this is done by using the `RiveUIViewRepresentable` and AsyncRiveUIViewRepresentable\` types.
```swift UIKit theme={null}
let riveView = RiveUIView({
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
return try await Rive(file: file)
})
view.addSubview(riveView)
```
```swift SwiftUI theme={null}
var body: some View {
// After having created a `Rive` object, you can initialize your view.
RiveUIViewRepresentable(rive)
}
```
```swift SwiftUI (Async) theme={null}
var body: some View {
// After having created a `Rive` object, you can initialize your view.
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
return try await Rive(file: file)
}
}
```
The above example uses the default artboard and state machine for the file, binding the default view model instance to the state machine if available. This is the default behavior when initializing a `Rive` object without specifying an artboard or state machine.
Alternatively, you can also specify which artboard and state machine to use. Documentation and examples for manually selecting which artboard to use is available in the [Artboards](/docs/runtimes/apple/artboards#choosing-an-artboard) documentation. Documentation and examples for manually selecting which state machine to use is available in the [State Machines](/docs/runtimes/apple/state-machines#getting-a-state-machine) documentation.
[Data Binding](/docs/runtimes/apple/data-binding) is a feature that allows you to dynamically update your Rive graphics from code. This includes things such as strings, numbers, booleans, and more.
By default, creating a `Rive` object will automatically data bind to the default view model instance for its artboard. However, there are three options for data binding:
```swift Auto Bind theme={null}
let file = try await File(...)
// Automaically find the (editor) default view model instance to bind to the Rive object's artboard.
// Below, the default artboard and state machine will be used, and the default view model instance will be bound to the state machine.
// This is the default value, if you do not pass in a dataBind argument.
let rive = try await Rive(file: file, dataBind: .auto)
// You can then access the bound view model instance via the `viewModelInstance` property.
let viewModelInstance = rive.viewModelInstance
```
```swift Bind Instance theme={null}
let file = try await File(...)
let viewModelInstance = try await file.createViewModelInstance(...)
// Bind the supplied view model instance to the Rive object's artboard.
// Below, the default artboard and state machine will be used, and the supplied view model instance will be bound to the state machine.
let rive = try await Rive(file: file, dataBind: .instance(viewModelInstance))
```
```swift None theme={null}
let file = try await File(...)
let artboard = try await file.createArtboard()
let stateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)
// If you have manually bound a view model instance to a state machine, you can opt-out of data binding.
stateMachine.bindViewModelInstance(viewModelInstance)
let rive = try await Rive(file: file, dataBind: .none)
```
Once data binding is set up, you can update and listen to data binding properties at runtime.
```swift Set Values theme={null}
let rive = try await Rive(...)
let viewModelInstance = rive.viewModelInstance
let stringProperty = StringProperty(path: "path/to/string")
viewModelInstance.setValue(of: stringProperty, to: "Hello, Rive")
```
```swift Get Values theme={null}
let rive = try await Rive(...)
let viewModelInstance = rive.viewModelInstance
let stringProperty = StringProperty(path: "path/to/string")
// Get the current value of the property
let value = try await viewModelInstance.value(of: stringProperty)
// Get a stream of values for the property, which emits a new value whenever the property changes.
let valueStream = viewModelInstance.valueStream(of: stringProperty)
do {
for try await value in valueStream {
print(value)
}
} catch let error as ViewModelInstanceError {
print(error)
} catch {
print(error)
}
```
For basic usage, see the [Marty](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/Concurrency/MartyView.swift) example in our Example app.
For a more complete example, see the [Quick Start](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/Concurrency/QuickStartView.swift) example in our Example app, which demonstrates how to use data binding.
For examples on how to pause and resume animations, as well as set frame rate, see the [Player](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/Concurrency/PlayerView.swift) example in our Example app.
**Swift Package Manager**
To install via Swift Package Manager, in the package finder in Xcode, search for `rive-ios` or the full Github path: `https://github.com/rive-app/rive-ios`
**Cocoapods**
Add the following to your Podspec file:
```bash theme={null}
pod 'RiveRuntime'
```
Add the following to the top of your file where you utilize the Rive runtime:
```swift theme={null}
import RiveRuntime
```
The primary object you'll use is a `RiveViewModel`. It is responsible for creating and interacting with Rive assets.
```swift Local File theme={null}
struct AnimationView: View {
var body: some View {
RiveViewModel(fileName: "cool_rive_animation").view()
}
}
```
```swift Remote URL theme={null}
struct AnimationView: View {
var body: some View {
RiveViewModel(
webURL: "https://cdn.rive.app/animations/off_road_car_v7.riv"
).view()
}
}
```
You can add Rive to a view controller purely with code by making the `RiveViewModel`, telling it to create a fresh `RiveView` and then adding it to the view hierarchy.
Alternatively, you can add Rive to a controller using Storyboards by making a `RiveViewModel`, and setting its view to be the `RiveView` you made in the Storyboard.
```swift Programmatic theme={null}
class AnimationViewController: UIViewController {
var simpleVM = RiveViewModel(fileName: "cool_rive_animation")
override func viewWillAppear(_ animated: Bool) {
let riveView = simpleVM.createRiveView()
view.addSubview(riveView)
riveView.frame = view.bounds
}
```
```swift Storyboard theme={null}
class AnimationViewController: UIViewController {
@IBOutlet weak var riveView: RiveView!
var simpleVM = RiveViewModel(fileName: "cool_rive_animation")
override public func viewDidLoad() {
simpleVM.setView(riveView)
}
}
```
## Playing / Pausing
```swift UIKit theme={null}
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
riveView.isPaused = true // or false to resume
```
```swift SwiftUI theme={null}
@State var isPaused = false
var body: some View {
RiveUIViewRepresentable(rive)
.paused(isPaused)
}
```
```swift SwiftUI (Async) theme={null}
@State var isPaused = false
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
return rive
}
.paused(isPaused)
}
```
```swift theme={null}
let viewModel = RiveViewModel(fileName: "...")
viewModel.pause() // or play() to resume
```
## Frame Rate
```swift UIKit theme={null}
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
// Set a fixed frame rate
riveView.frameRate = .fps(30)
// Set a range of frame rates (Note: on < iOS 15 / macOS 14, this is the same as using .fps)
riveView.frameRate = .range(minimum: 30, maximum: 60)
// Revert to the default frame rate
riveView.frameRate = .default
```
```swift SwiftUI theme={null}
var body: some View {
RiveUIViewRepresentable(rive)
.frameRate(.fps(30))
// or
.frameRate(.range(minimum: 30, maximum: 60))
// or
.frameRate(.default)
}
```
```swift SwiftUI (Async) theme={null}
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
return rive
}
.frameRate(.fps(30))
// or
.frameRate(.range(minimum: 30, maximum: 60))
// or
.frameRate(.default)
}
```
```swift theme={null}
let viewModel = RiveViewModel(fileName: "...")
viewModel.setPreferredFramesPerSecond(preferredFramesPerSecond: 30)
// or, on iOS >= 15 / macOS >= 14
viewModel.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 60))
```
## Semantics
Rive views can expose [semantics defined in the editor](/docs/editor/accessibility/semantics) to VoiceOver. Semantics are available in the New Runtime only.
See [Semantics](/docs/runtimes/apple/semantics) for the available modes and more detail.
## Threading
The new runtime supports multi-threading through the introduction of `Worker` objects.
```swift theme={null}
// In an async context
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
```
The `Worker` object is responsible for creating and managing the background thread for the Rive instance.
Each worker shares out-of-band assets, such as images, fonts, and audio. This means that each `File` initialized with the same worker will share the same out-of-band assets.
One worker roughly equates to one background thread. If you are rendering multiple heavy Rive graphics, you can create one `Worker` per file to have each processed on its own background thread.
The number of `Worker` objects is limited by system availability; specifically, a `Worker` is backed by a `DispatchQueue`, which handles the creation and reuse of threads.
It is important to note that while multi-threading is supported, Rive object API calls must still be made on the main actor. This is enforced at compile time with functions and types marked as `@MainActor`.
### Shared Workers
For cases where you want to share resources between multiple `File` objects (i.e out-of-band / referenced assets), you can use a shared `Worker` object. An example implementation is shown below.
```swift theme={null}
actor WorkerProvider {
static let shared = WorkerProvider()
@MainActor
private var cachedWorker: Worker?
@MainActor
func worker() async throws -> Worker {
if let cachedWorker {
return cachedWorker
}
let worker = try await Worker()
cachedWorker = worker
return worker
}
}
```
Then, when creating a `Rive` object, you can use the shared worker provider to get a worker.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
```
The legacy runtime is currently single-threaded on the main thread. This means that all Rive calls must be made on the main thread. It is recommended that if you are on a background thread, you should dispatch to the main queue before making any Rive calls.
## Logging
The new runtime does not yet include logging, but will be added in the near future.
Enabling logging is as simple as setting `RiveLogger.isEnabled` to `true`.
```swift theme={null}
RiveLogger.isEnabled = true
```
For more details on logging levels, categories, and verbose logs, see the [Logging](/docs/runtimes/logging) page.
See subsequent runtime pages to learn how to control animation playback, state machines, and more.
## Example App
You can run our Apple example app from the Rive GitHub repository.
```bash theme={null}
git clone https://github.com/rive-app/rive-ios
```
Open the `Example-iOS` app in Xcode and be sure to select the `Preview (iOS)` or `Preview (macOS)` [scheme](https://developer.apple.com/documentation/xcode/customizing-the-build-schemes-for-a-project). The other schemes are for development purposes and require additional configuration, see[ ](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md)[CONTRIBUTING.MD](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md).
## Resources
GitHub: [https://github.com/rive-app/rive-ios](https://github.com/rive-app/rive-ios)
Examples:
* [https://github.com/rive-app/rive-ios/tree/main/Example-iOS](https://github.com/rive-app/rive-ios/tree/main/Example-iOS)
* [https://github.com/rive-app/rive-ios/tree/main/Demo-App](https://github.com/rive-app/rive-ios/tree/main/Demo-App)
* Free course from Meng To: [https://designcode.io/swiftui-rive](https://designcode.io/swiftui-rive)
# Artboards
Source: https://rive.app/docs/runtimes/apple/artboards
Selecting which artboard to render at runtime
For more information on creating artboards in the Rive editor, please refer to [Artboards](/docs/editor/fundamentals/artboards).
## Choosing an Artboard
When a Rive object is instantiated or when a Rive file is rendered, you can specify the artboard to use. If no artboard is given, the [default artboard](/docs/editor/fundamentals/artboards#default-state-machine), as set in the Rive editor, is used. If no default artboard is set, the first artboard is used.
Only one artboard can be rendered at a time.
The following section assumes that you have read through the [Apple](/docs/runtimes/apple/apple) overview.
### Getting an Artboard
Once you have created a `File`, you can then retrieve information for and create `Artboard` types.
```swift theme={null}
// Get all artboard names in the file
let artboardNames = try await file.getArtboardNames()
// Get the default artboard for the file
let defaultArtboard = try await file.createArtboard()
// Get an artboard by name from the file
let artboardByName = try await file.createArtboard("Artboard")
```
Note that these are all async throwing functions marked as `@MainActor`. Since they are functions called on a `File` object, any thrown errors will be of type `FileError`.
An example of when one of these functions will throw is if you call `.createArtboard(_:)` with a name that is not in the origin `File`, which will throw a `FileError.invalidArtboard(String)`.
### Using an Artboard
Remember that the Rive configuration for a view is the `Rive` type. In the overview, we show initializing a `Rive` object with a file, opting to use the default artboard and state machine. However, you can initialize a `Rive` object with a specific artboard:
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
let artboardByName = try await file.createArtboard("Artboard")
let rive = try await Rive(file: file, artboard: artboardByName)
```
Artboards then become the source of truth for state machines. See [State Machine](/docs/runtimes/apple/state-machines) for more details.
**SwiftUI**
```swift theme={null}
struct AnimationView: View {
var body: some View {
RiveViewModel(
fileName: "my_rive_file",
artboardName: "My Artboard"
).view()
}
}
```
**UIKit**
```swift theme={null}
class AnimationViewController: UIViewController {
@IBOutlet weak var riveView: RiveView!
var bananaVM = RiveViewModel(
fileName: "my_rive_file",
artboardName: "My Artboard",
)
override func viewDidLoad() {
bananaVM.setView(riveView)
}
}
```
# Caching a Rive File
Source: https://rive.app/docs/runtimes/apple/caching-a-rive-file
Under most circumstances a `.riv` file should load quickly and managing the `RiveFile` yourself is not necessary. But if you intend to use the same `.riv` file in multiple parts of your application, or even on the same screen, it might be advantageous to load the file once and keep it in memory.
## Example Usage
To cache a Rive file, you can create a strong reference to a `File` object. This `File` can then be reused to create `Rive` objects.
Artboards and state machines are unique when created using the `create` functions. This means that you can create a new `Rive` object with the same file, but with different (and unique) artboards and state machines.
```swift theme={null}
// An example builder class that creates a new Rive object with a cached file, creating new and unique artboards and state machines for each Rive object.
class RiveBuilder {
private let file: File
init(file: File) {
self.file = file
}
@MainActor
func createRive(artboard: String? = nil, stateMachine: String? = nil) async throws -> Rive {
let artboardInstance = try await file.createArtboard(artboard ?? .default)
let stateMachineInstance = try await artboardInstance.createStateMachine(stateMachine ?? .default)
return try await Rive(file: file, artboard: artboardInstance, stateMachine: stateMachineInstance)
}
}
// Load and cache the file once
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
// Create a builder with the cached file
let builder = RiveBuilder(file: file)
// Create multiple Rive objects with different configurations
// Each Rive object is unique, but they all share the same cached File
let rive1 = try await builder.createRive() // Creates a unique artboard and state machine
let rive2 = try await builder.createRive() // Creates a unique artboard and state machine, behaving separately from the first Rive object
let rive4 = try await builder.createRive(artboard: "MainArtboard", stateMachine: "Walking") // Creates a unique artboard and state machine, behaving separately from the first three Rive objects
let rive5 = try await builder.createRive(artboard: "MainArtboard", stateMachine: "Idle") // Creates a unique artboard and state machine, behaving separately from the first four Rive objects
```
```swift theme={null}
// Cache a RiveFile somewhere to cache for reuse
let file = try! RiveFile(resource: "file", loadCdn: false)
// For example purposes, a type that reuses a single RiveFile
// when creating new view models for given state machines or artboards.
class ViewModelGenerator {
/// The RiveFile to reuse when generating new view models.
private let file: RiveFile
init(file: RiveFile) {
self.file = file
}
// Returns a new view model using a cached RiveFile.
// This means that the RiveFile will not have to be reparsed
// each time a view model is generated.
func viewModel(stateMachine: String?, artboard: String?) -> RiveViewModel {
// While one RiveFile can be cached and reused, each view model
// should have its own model as to not share state.
let model = RiveModel(riveFile: file)
return RiveViewModel(model, stateMachineName: stateMachine, artboardName: artboard)
}
}
```
When using the `RiveViewModel(fileName:)` initializer, the Apple runtime does not cache file usage; that has to be handled manually. You may find that when reusing the same file, your memory usage increases (over time) as you create more view models. This is when you should consider caching the underlying file for reuse.
Reusing a single `RiveFile` (when applicable) will reduce the overall memory usage of your application. If your `.riv` can be reused across multiple views, where each view requires the same file but uses different artboards or state machines, consider caching the `RiveFile` before creating your view models. While one `RiveFile` can be cached, to ensure that each view is in its own state, you must create a unique `RiveModel` per `RiveViewModel` instance.
# Data Binding
Source: https://rive.app/docs/runtimes/apple/data-binding
Connect your code to bound editor elements using View Models
Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the [Overview](/docs/editor/data-binding/overview).
# 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](#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.
View models are not their own type; rather, they are a source when creating a view model instance from a `File`.
You can define the source of a view model via the `ViewModelSource` type.
```swift theme={null}
case artboardDefault(Artboard) // References the default view model for an Artboard
case name(String) // References a view model from a file by name
```
These sources are used in conjunction with getting a view model instance. See [View Model Instances](#view-model-instances) for more information.
```swift theme={null}
let riveViewModel = RiveViewModel(...)
let file = riveViewModel.riveModel!.riveFile
// Data binding view model by name
let viewModelByName = file.viewModelNamed("...")
// Data binding view model by index
for index in 0..
# 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:
| Type | Value |
| ----------------- | --------------- |
| Number | 0 |
| String | Empty string |
| Boolean | False |
| Color | 0xFF000000 |
| Trigger | Untriggered |
| Enum | The first value |
| Image | No image |
| Font | No font |
| Artboard | No artboard |
| List | Empty list |
| Nested view model | Null |
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.
In some samples, due to the wordiness of "view model instance", we use the abbreviation "VMI", as well as "VM" for "view model".
The following section assumes that you have read through the [Apple](/docs/runtimes/apple/apple) overview.
```swift theme={null}
// From a file
let file: File = ...
// When using a view model by name:
// A blank view model instance
var blankInstance = try await file.createViewModelInstance(.blank(from: .name("ViewModel")))
// The default instance for the view model
var defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .name("ViewModel")))
// An instance by name from the view model
var namedInstance = try await file.createViewModelInstance(.name("Instance", from: .name("ViewModel")))
// Alternatively, using the default view model for an artboard
let artboard: Artboard = ...
// A blank view model instance
blankInstance = try await file.createViewModelInstance(.blank(from: .artboardDefault(Artboard)))
// The default instance for the view model
defaultInstance = try await file.createViewModelInstance(.viewModelDefault(from: .artboardDefault(Artboard)))
// An instance by name from the view model
namedInstance = try await file.createViewModelInstance(.name("Instance", from: .artboardDefault(Artboard)))
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!
// Create blank
let blankInstance = viewModel.createInstance()
// Create default
let defaultInstance = viewModel.createDefaultInstance()
// Create by index
for index in 0..
### Binding
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.
Given the following example code:
```swift theme={null}
let file: File = ...
let artboard: Artboard = try await file.createArtboard()
let stateMachine: StateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)
```
You can manually bind the view model instance to the state machine:
```swift theme={null}
stateMachine.bindViewModelInstance(viewModelInstance)
```
Alternatively, you can utilize the `Rive` type to (automatically) data bind a view model instance:
```swift theme={null}
// Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument.
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// Bind a view model instance
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .viewModelInstance(viewModelInstance))
// Do not bind. This assumes you have manually bound a view model instance earlier
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .none)
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
let artboard = riveViewModel.riveModel!.artboard,
let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()!
// Apply the instance to the state machine (preferred)
// Applying to a state machine will automatically bind to its artboard
riveViewModel.riveModel!.stateMachine.bind(instance)
// Alternatively, apply the instance to the artboard
artboard.bind(viewModelInstance: instance)
```
### 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.
Given the following example code:
```swift theme={null}
let file: File = ...
let artboard: Artboard = try await file.createArtboard()
let stateMachine: StateMachine = try await artboard.createStateMachine()
let viewModelInstance = try await file.createViewModelInstance(...)
```
When creating a `Rive` object, you can elect to auto bind:
```swift theme={null}
// Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument.
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto)
// or
var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine)
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to `instance` to later access properties
// The instance may change as state machines and artboards change
}
// If you'd like to disable autoBind after enabling…
riveViewModel.riveModel!.disableAutoBind()
```
# 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:
| Type | Supported |
| ---------------------- | --------- |
| Floating point numbers | ✅ |
| Booleans | ✅ |
| Triggers | ✅ |
| Strings | ✅ |
| Enumerations | ✅ |
| Colors | ✅ |
| Nested View Models | ✅ |
| Lists | ✅ |
| Images | ✅ |
| Artboards | ✅ |
For more information on version compatibility, see the [Feature Support](/docs/feature-support) page.
### Listing Properties
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.
```swift theme={null}
let file: File = ...
let properties = try await file.getProperties(of: "ViewModel")
for property in properties {
print(property.type) // enum of string, number, boolean, etc
print(property.name) // The name of the property within the view model
print(property.metaData) // Additional metadata for the property, if available
}
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)!
for property in viewModel.properties {
print(property.type) // String, number, boolean, etc
print(property.name) // The name of the property within the view model
}
```
### Reading and Writing 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.
Property types are a very thin wrapper around the path and return type of a property.
All property APIs (e.g setters, getters, and triggers) are available as part of a `ViewModelInstance` object.
```swift String theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
// String
let stringProperty = StringProperty(path: "path/to/string")
let stringValue = try await viewModelInstance.value(of: stringProperty)
viewModelInstance.setValue(of: stringProperty, to: "value")
```
```swift Number theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
let numberProperty = NumberProperty(path: "path/to/number")
let numberValue = try await viewModelInstance.value(of: numberProperty)
viewModelInstance.setValue(of: numberValue, to: 9001)
```
```swift Bool theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
let boolProperty = BoolProperty(path: "path/to/bool")
let boolValue = try await viewModelInstance.value(of: boolProperty)
viewModelInstance.setValue(of: boolProperty, to: true)
```
```swift Color theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
let colorProperty = ColorProperty(path: "path/to/color")
let colorValue = try await viewModelInstance.value(of: colorProperty)
viewModelInstance.setValue(of: colorProperty, to: Color(red: 255, green: 255, blue: 255, alpha: 255))
```
```swift Enum theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
let enumProperty = EnumProperty(path: "path/to/enum")
let enumValue = try await viewModelInstance.value(of: enumProperty)
viewModelInstance.setValue(of: enumProperty, to: "value")
```
```swift Trigger theme={null}
let file: File = ...
let viewModelInstance = try await file.createViweModelInstance(...)
let triggerProperty = TriggerProperty(path: "path/to/trigger")
viewModelInstance.fire(trigger: triggerProperty)
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Strings
let stringProperty = instance.stringProperty(fromPath: "...")!
// Updating its value
stringProperty.value = "Hello, Rive"
// Get its value
print(stringProperty.value)
// You can also set and get values without storing a strong reference
instance.stringProperty(fromPath: "...").value = "Hello again, Rive"
// Numbers
let numberProperty = instance.numberProperty(fromPath: "...")!
// Updating its value
numberProperty.value = 1337
// Get its value
print(numberProperty.value)
// You can also set and get values without storing a strong reference
instance.numberProperty(fromPath: "...").value = 1337
// Booleans
let booleanProperty = instance.booleanProperty(fromPath: "...")!
// Updating its value
booleanProperty.value = true
// Get its value
print(booleanProperty.value)
// You can also set and get values without storing a strong reference
instance.booleanProperty(fromPath: "...").value = true
// Colors
let colorProperty = instance.colorProperty(fromPath: "...")!
// Updating its value, which is a UIColor/NSColor, so all static helpers apply.
colorProperty.value = .red
// Get its value
print(colorProperty.value)
// You can also set and get values without storing a strong reference
instance.colorProperty(fromPath: "...").value = .red
// Enums
let enumProperty = instance.enumProperty(fromPath: "...")!
// Updating its value
enumProperty.value = "Foo"
// Get its value
print(enumProperty.value)
// Print all possible values
print(enumProperty.values)
// You can also set and get values without storing a strong reference
instance.enumProperty(fromPath: "...").value = "Foo"
// Trigger
let triggerProperty = instance.triggerProperty(fromPath: "...")!
// Fire the trigger
triggerProperty.trigger()
```
### 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.
Property types are no longer reference types, and require the name or full path to a property when initializing the property value type. There is no longer an API to chain nested properties.
See [Properties](#properties) for usage details.
```swift theme={null}
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
let nestedNumberByChain = instance
.viewModelInstanceProperty(fromPath: "Nested View Model")
.viewModelInstanceProperty(fromPath: "Another Nested View Model")
.numberProperty(fromPath: "Number")
let nestedNumberByPath = instance.numberProperty(fromPath: "Nested View Model/Another Nested View Model/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.
Property listeners utilize Swift Concurrency's async throwing stream API. If a property returns a value, you can listen to its changes by calling the `valueStream(of:)` method on a `ViewModelInstance` object.
```swift theme={null}
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let stringProperty = StringProperty(path: "path/to/string")
let valueStream = viewModelInstance.valueStream(of: stringProperty)
do {
for try await value in valueStream {
print(value)
}
} catch let error as ViewModelInstanceError {
// The thrown error should always be a ViewModelInstanceError type
print(error)
} catch {
print(error)
}
```
For triggers, you can listen to them by calling the `stream(of:)` method on a `ViewModelInstance` object. This returns a stream of `Void` values, which can be ignored.
```swift theme={null}
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let triggerProperty = TriggerProperty(path: "path/to/trigger")
let triggerStream = viewModelInstance.stream(of: triggerProperty)
do {
for try await _ in triggerStream {
print("Trigger fired!")
}
} catch let error as ViewModelInstanceError {
// The thrown error should always be a ViewModelInstanceError type
print(error)
} catch {
print(error)
}
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Get the string property
let stringProperty = instance.stringProperty(fromPath: "...")!
// Add a listener
let listener = stringProperty.addListener { newValue in
print(newValue)
}
// Remove a listener, where listener is the return value of addListener
stringProperty.removeListener(listener)
// Trigger properties can also be listened to for when they are triggered
instance.triggerProperty(fromPath: "...")!.addListener {
print("Triggered!")
}
```
### Images
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.
To set an image, you first need to decode an image from a `Worker`. This has to be the `Worker` that was used when initializing a `File`, from which you are setting the image property of a view model instance.
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let viewModelInstance = file.createViewModelInstance(...)
let imageProperty = ImageProperty(path: "path/to/image")
let imageData: Data = ...
let image = try await decodeImage(from: imageData)
viewModelInstance.setValue(of: imageProperty, to: image)
```
```swift theme={null}
let riveViewModel = RiveViewModel(...)
var viewModelInstance: RiveDataBindingViewModel.Instance!
// You can get the view model instance when enabling auto binding
riveViewModel.riveModel?.enableAutoBind { instance in
// Store a reference to instance
viewModelInstance = instance
}
// Alternatively, you can create a view model instance manually
viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!
// Create a RiveRenderImage from data
let data = Data(...)
var image = RiveRenderImage(data: data)! // This can return nil if the data is not a valid image
// Or, create a RiveRenderImage from a UIImage
image = RiveRenderImage(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 properties let you manage a dynamic set of view model instances at runtime. For example, you can build a to-do app where users can add and remove tasks in a scrollable Layout.
See the [Editor section](/docs/editor/data-binding/lists) on creating data bound lists.
A single list property can include different view model types, with each view model tied to its own Component, making it easy to populate a list with a variety of Component instances.
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](/docs/editor/data-binding/lists#view-model-list-property) editor documentation.
```swift theme={null}
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let listProperty = ListProperty(path: "path/to/list")
let size = try await viewModelInstance.size(of: listProperty)
// Result: [newInstance]
let newInstance = try await file.createViewModelInstance(...)
viewModelInstance.appendInstance(newInstance, to: listProperty)
// Result: [insertedInstance, newInstance]
let insertedInstance = try await file.createViewModelInstance(...)
viewModelInstance.insertInstance(instance, to: listProperty, at: 0)
// Result: [newInstance, insertedInstance]
viewModelInstance.swapInstance(atIndex: 0, withIndex: 1, in: listProperty)
// Result: [newInstance]
viewModelInstance.removeInstance(at: 1, from: listProperty)
// Result: newInstance
let _ = viewModelInstance.value(of: listProperty, at: 0)
// Result: []
viewModelInstance.removeInstance(newInstance, from: listProperty)
// Result: 0
let size = try await viewModelInstance.size(of: listProperty)
```
```swift theme={null}
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
```
### Artboards
Artboard properties allows you to swap out entire components at runtime. This is useful for creating modular components that can be reused across different designs or applications, for example:
* Creating a skinning system that supports a large number of variations, such as a character creator where you can swap out different body parts, clothing, and accessories.
* Creating a complex scene that is a composition of various artboards loaded from various different Rive files (drawn to a single canvas/texture/widget).
* Reducing the size (complexity) of a single Rive file by breaking it up into smaller components that can be loaded on demand and swapped in and out as needed.
```swift theme={null}
let file: File = ...
let viewModelInstance = try await file.createViewModelInstance(...)
let artboardProperty = ArtboardProperty(path: "path/to/artboard")
let artboard = try await file.createArtboard(...)
viewModelInstance.setValue(of: artboardProperty, to: artboard)
```
Use the `artboardProperty` method on a `RiveDataBindingViewModel.Instance` object to get the artboard property.
Then use the `setValue` method on the artboard property object to set the new artboard value.
`setValue` accepts a `RiveBindableArtboard` object, which is a wrapper for an artboard that can be used to set the artboard property value.
You can get a `RiveBindableArtboard` object by using the `bindableArtboard` methods on a `RiveFile` object.
```swift theme={null}
let artboardProperty = instance.artboardProperty(fromPath: "Artboard")!
let components = RiveFile(...)
let bindableArtboard = components.bindableArtboard(at: 0)!
let bindableArtboard2 = components.bindableArtboard(withName: "...")!
artboardProperty.setValue(bindableArtboard)
```
### 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.
# Examples
See the [Data Binding view](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/SwiftUI/DataBindingView.swift) in the Example app for a demo.
# FAQ
Source: https://rive.app/docs/runtimes/apple/faq
# How do I enable support for ProMotion displays?
Support for ProMotion on iOS requires two things:
1. Using an API in the Apple runtime to set the desired FPS (range)
2. Adding an additional entry into your app's`Info.plist` file
## Example Usage
```swift theme={null}
let preferredFPS = UIScreen.main.maximumFramesPerSecond
// or
let preferredFPSRange = CAFrameRateRange(minimum: 60, maximum: Float(preferredFPS))
let viewModel = {
let viewModel = RiveViewModel(fileName: "...")
viewModel.setPreferredFramesPerSecond(preferredFramesPerSecond: preferredFPS)
// or
viewModel.setPreferredFrameRateRange(preferredFPSRange)
return viewModel
}()
```
Additionally, add the following to your app's `Info.plist` file:
`CADisableMinimumFrameDurationOnPhone`
You can view more information about preferred FPS [here](https://developer.apple.com/documentation/quartzcore/cadisplaylink/1648421-preferredframespersecond), and about preferred FPS range [here](https://developer.apple.com/documentation/quartzcore/cadisplaylink/3875343-preferredframeraterange).
# Why is resource usage different compared to other libraries?
See our [Resource Usage](/docs/runtimes/apple/resource-usage) documentation for more details.
# Fonts
Source: https://rive.app/docs/runtimes/apple/fonts
Loading and replacing fonts dynamically at runtime.
## Swapping Font Assets at Runtime
Fonts can be loaded dynamically at runtime. This allows you to localize your Rive content without increasing the file size of the exported .riv file.
Swapping a font asset replaces all instances of the font.
For more information, see [Loading Assets](/docs/runtimes/apple/loading-assets).
## Fallback Fonts
When rendering text, not all glyphs (characters) may be available in the active font. This commonly occurs when:
* Using custom fonts that don’t support all languages or Unicode ranges
* The embedded font is a subset of the font
* User-generated or dynamic text contains unexpected characters
A fallback font is used automatically when the primary font cannot render a specific glyph. These are typically system fonts, which generally provide broad Unicode coverage.
On iOS, font sizes specified for fallback fonts are ignored. Instead, the platform selects system fonts that best match the styling and animation of the 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.
```swift theme={null}
// 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.
```swift theme={null}
// 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)
]
}
}
```
# Layout
Source: https://rive.app/docs/runtimes/apple/layouts
Control how graphics are laid out within the canvas.
## The Fit Mode
A Rive graphic authored in the editor will not necessarily match the size of the container it is rendered into at runtime. We need to determine the behavior for this scenario, as no one size fits all.
The solution is choosing the fit mode. This is specified on the container and controls how Rive is scaled.
* `Layout`: Use the Rive layout engine to apply responsive layout to the artboard, matching the container dimensions. For this to work, the artboard must be designed with layouts in mind. See [Responsive Layouts](#responsive-layouts) for more information on how to use this option.
* `Contain`: **(Default)** Preserve aspect ratio and scale the artboard so that its larger dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will leave space on the shorter dimension's axis.
* `ScaleDown`: Preserve aspect ratio and behave like `Contain` when the artboard is larger than the container. Otherwise, use the artboard's original dimensions.
* `Cover`: Preserve aspect ratio and scale the artboard so that its smaller dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will clip the artboard on the larger dimension's axis.
* `FitWidth`: Preserve aspect ratio and scale the artboard width to match the container's width.
If the aspect ratios between the artboard and container do not match, this will result in either vertical clipping or space in the vertical axis.
* `FitHeight`: Preserve aspect ratio and scale the artboard height to match the container's height.
If the aspect ratios between the artboard and container do not match, this will result in either horizontal clipping or space in the horizontal axis.
* `Fill`: Do not preserve aspect ratio and stretch to the container's dimensions.
* `None`: Do not scale. Use the artboard's original dimensions.
For either dimension, if the artboard's dimension is larger, it will be clipped. If it is smaller, it will leave space.
### Alignment
In all options other than `Layout` and `Fill`, there is the possibility that the Rive graphic is clipped or leaves space within its container. Alignment determines how content aligns within the container. The following options are available.
* `TopLeft`
* `TopCenter`
* `TopRight`
* `CenterLeft`
* `Center` **(Default)**
* `CenterRight`
* `BottomLeft`
* `BottomCenter`
* `BottomRight`
### Applying the Fit Mode
You can set the fit and layout options on a `Rive` object. The `.fit` can be updated at runtime without creating a new `Rive` object.
For all possible options, see [Fit.swift](https://github.com/rive-app/rive-ios/blob/main/Source/Concurrency/View/Fit.swift)
```swift theme={null}
// Set a fit and alignment for an artboard that does not use layouts
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
var rive = try await Rive(file: file, fit: .contain(alignment: .center))
// Update the fit and alignment at runtime
rive.fit = .fitWidth(alignment: .topCenter)
```
The runtime provides the following enums to set on layout parameters:
* **Fit**
* `.fill`
* `.contain`
* `.cover`
* `.fitWidth`
* `.fitHeight`
* `.scaleDown`
* `.noFit`
* **Alignment**
* `.topLeft`
* `.topCenter`
* `.topRight`
* `.centerLeft`
* `.center`
* `.centerRight`
* `.bottomLeft`
* `.bottomCenter`
* `.bottomRight`
### SwiftUI
The following example shows how to set layout parameters and switch them at runtime:
```swift theme={null}
struct SwiftLayout: View {
@State private var fit: RiveFit = .contain
@State private var alignment: RiveAlignment = .center
var body: some View {
VStack {
RiveViewModel(fileName: "fancy_rive_file", fit: fit, alignment: alignment).view()
}
HStack {
Text("Some Fit Examples")
}
HStack {
Button("Fill") { fit = .fill }
Button("Contain") { fit = .contain }
Button("Cover") { fit = .cover }
}
HStack {
Text("Some Alignment Examples")
}
HStack {
Button("Top Left") { alignment = .topLeft }
Button("Top Center") { alignment = .topCenter }
Button("Top Right") { alignment = .topRight }
}
}
}
```
### UIKit
The following example shows how to set layout parameters and switch them at runtime:
```swift theme={null}
class LayoutViewController: UIViewController {
@IBOutlet weak var riveView: RiveView!
var viewModel = RiveViewModel(fileName: "fancy_rive_file")
override func viewDidLoad() {
viewModel.setView(riveView)
}
@IBAction func fitButtonTriggered(_ sender: UIButton) {
setFit(name: sender.currentTitle!)
}
@IBAction func alignmentButtonTriggered(_ sender: UIButton) {
setAlignment(name: sender.currentTitle!)
}
func setFit(name: String) {
var fit: RiveFit = .contain
switch name {
case "Fill": fit = .fill
case "Contain": fit = .contain
case "Cover": fit = .cover
case "Fit Width": fit = .fitWidth
case "Fit Height": fit = .fitHeight
case "Scale Down": fit = .scaleDown
case "None": fit = .noFit
default: fit = .contain
}
viewModel.fit = fit
}
func setAlignment(name: String) {
var alignment: RiveAlignment = .center
switch name {
case "Top Left": alignment = .topLeft
case "Top Center": alignment = .topCenter
case "Top Right": alignment = .topRight
case "Center Left": alignment = .centerLeft
case "Center": alignment = .center
case "Center Right": alignment = .centerRight
case "Bottom Left": alignment = .bottomLeft
case "Bottom Center": alignment = .bottomCenter
case "Bottom Right": alignment = .bottomRight
default: alignment = .center
}
viewModel.alignment = alignment
}
}
```
## Responsive Layouts
Rive’s layout feature lets you design resizable artboards with built-in responsive behavior, configured from the editor. Ensure the fit mode is set to **Layout** at runtime and the artboard will resize to fill its container according to the constraints defined in the editor.
Optionally you may provide a **layout scale factor** to multiply the scale of the content. This allows fine tuning the visual size within your container. This property only applies when setting the **Fit** mode to **Layout**.
For more Editor information and how to configure your graphic, see [Layouts Overview](/docs/editor/layouts/layouts-overview).
When creating a new `Rive` object, you can set the fit to layout, with two options for the scale factor: automatic or explicit.
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
// Create a new Rive object with a layout fit that automatically determines the scale factor based on the screen the view is being displayed on
var rive = try await Rive(file: file, fit: .layout(scaleFactor: .automatic))
// Or, use an explicit scale factor
rive.fit = .layout(scaleFactor: .explicit(2.0))
```
**Examples**
* [SwiftUI](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/SwiftUI/SwiftLayout.swift)
**Steps**
1. Set `fit` on an instance of `RiveViewModel` to `layout`
2. Optionally set `layoutScaleFactor` on `RiveViewModel` for manual control of an artboard's scale factor.
To enable automatically determining the scale factor, set `.layoutScaleFactor` to `RiveViewModel.layoutScaleFactorAutomatic`. This is the default value; it is equivalent to `-1`. When set, Rive will listen for window and screen changes for the view model's view, and automatically apply the correct scale factor for the current view hierarchy.
```swift theme={null}
let viewModel = RiveViewModel(fileName: "...")
viewModel.fit = .layout
viewModel.layoutScaleFactor = RiveViewModel.layoutScaleFactorAutomatic // Allow Rive to determine the scale factor
viewModel.layoutScaleFactor = 2.0 // Or, explicitly set the scale factor
```
# Loading Assets
Source: https://rive.app/docs/runtimes/apple/loading-assets
Loading and replacing assets dynamically at runtime
If you want to dynamically replace images, use image data binding.
Some Rive files may contain assets that can be embedded within the actual file binary, such as font, image, or audio files. The Rive runtimes may then load these assets when the Rive file is loaded. While this makes for easy usage of the Rive files/runtimes, there may be opportunities to load these assets in or even replace them at runtime instead of embedding them in the file binary.
There are several benefits to this approach:
* Keep the `.riv` files tiny without potential bloat of larger assets
* Dynamically load an asset for any reason, such as loading an image with a smaller resolution if the `.riv` is running on a mobile device vs. an image of a larger resolution for desktop devices
* Preload assets to have available immediately when displaying your `.riv`
* Use assets already bundled with your application, such as font files
* Sharing the same asset between multiple `.riv`s
## Methods for Loading Assets
There are currently three different ways to load assets for your Rive files.
In the Rive editor select the desired asset from the **Assets** tab, and in the inspector choose the desired export option:
### Embedded Assets
In the Rive editor, static assets can be included in the `.riv` file, by choosing the *"Embedded"* export type. As stated in the beginning of this page, when the Rive file gets loaded, the runtime will implicitly attempt to load in the assets embedded in the `.riv` as well, and you don't need to concern yourself with loading any assets manually.
**Caveat:** Embedded assets may bulk up the file size, especially when it comes to fonts when using Rive Text ([Text Overview](/docs/editor/text/text-overview)).
**Embedded is the default option.**
### Image CDNs
Some image CDNs allow for on-the-fly image transformations, including resizing, cropping, and automatic format conversion based on the browser's and device's capabilities. These CDNs can host your Rive image assets. Note that for these CDNs, you may need to specify the accepted formats, for example, as part of the HTTP header request:
```html theme={null}
... headers: { Accept: 'image/png,image/webp,image/jpeg,*/*', } ...
```
Please see your CDN provider's documentation for additional information.
Rive supports the following image formats: **jpeg**, **png**, and **webp**
### Referenced Assets
In the Rive editor, you can mark an imported asset as a *"Referenced"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, and the responsibility of loading the asset will be handled by your application at runtime.
This option enables you to dynamically load in assets via a handler API when the runtime begins loading in the `.riv` file. This option is preferable if you have a need to dynamically load in a specific asset based on any kind of app/game logic, and especially if you want to keep the .riv file size small.
All referenced assets, including the `.riv`, will be bundled as a zip file when you export your animation.
**Caveat:** You will need to provide an asset handler API when loading in Rive which should do the work of loading in an asset yourself. See [Handling Assets](#handling-assets).
SVG assets can't currently be loaded at runtime as referenced assets.
This is because SVGs are converted to Rive vector objects, which are always embedded into the .riv.
## Handling Assets
This section assumes that you have read through the [Apple](/docs/runtimes/apple/apple) overview.
Referenced assets can be added using the global asset APIs of a `Worker`.
Adding global assets is a two-step process:
1. Decode the asset from its bytes
2. Add the asset to the worker
Global assets are push-based, meaning that you have to know the unique name of the asset ahead of time. You can retrieve the unique name of the asset from the exported `.zip` file containing the assets. The unique identifier is the identifier that is appended to the asset name (e.g `Font-1234`).
There is no need to maintain a strong reference to the asset. The asset will be automatically cleaned up when the worker is disposed.
```swift Font theme={null}
let worker = try await Worker()
let fontData: Data = ...
let font = try await worker.decodeFont(from: fontData)
worker.addGlobalFontAsset(font, name: "MyFont-1234")
// If you would like to clean up the asset
worker.removeGlobalFontAsset(name: "MyFont-1234")
```
```swift Audio theme={null}
let worker = try await Worker()
let audioData: Data = ...
let font = try await worker.decodeAudio(from: audioData)
worker.addGlobalAudioAsset(audio, name: "MyAudio-1234")
// If you would like to clean up the asset
worker.removeGlobalAudioAsset(name: "MyAudio-1234")
```
```swift Image theme={null}
let worker = try await Worker()
let imageData: Data = ...
let image = try await worker.decodeImage(from: imageData)
worker.addGlobalImageAsset(image, name: "MyImage-1234")
// If you would like to clean up the asset
worker.removeGlobalImageAsset(name: "MyImage-1234")
```
### Examples
* [(SwiftUI) Swap out images and fonts](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/SwiftUI/SwiftSimpleAssets.swift)
* [(UIKit) Swap and cache images and fonts](https://github.com/rive-app/rive-ios/blob/main/Example-iOS/Source/Examples/Storyboard/CachedAssets.swift)
### Using the Asset Handler API
When instantiating a `RiveViewModel` (or `RiveFile` directly), add a `customLoader` callback property to the list of parameters. This callback will be called for every asset the runtime detects from the `.riv` file on load, and the callback will be responsible for either handling the load of an asset at runtime or passing on the responsibility and giving the runtime a chance to load it otherwise.
An instance where you may want to handle loading an asset is if an asset in the file is marked as **Referenced**, and you need to provide an actual asset to render for the graphic, as Rive does not embed it in the `.riv` and thus cannot load it.
An instance where you may want to give the runtime a chance to load the asset is if the asset in the file is marked as **Hosted**, and want to pass the responsibility of loading it to the runtime (which will call into a Rive CDN to do so).
```swift theme={null}
RiveViewModel(fileName: "simple_assets", loadCdn: false, customLoader: { (asset: RiveFileAsset, data: Data, factory: RiveFactory) -> Bool in
// A simple check for a Rive file with one asset
if (asset is RiveImageAsset){
// picture-47982.jpeg can be exported with the .riv file from the Rive editor.
// It is then included in the main bundle resources of the project
guard let url = (.main as Bundle).url(forResource: "picture-47982", withExtension: "jpeg") else {
fatalError("Failed to locate 'picture-47982' in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}
(asset as! RiveImageAsset).renderImage(
factory.decodeImage(data)
)
return true;
}
return false;
}).view()
```
Your provided callback will be passed an `asset`, `data`, and a `factory`.
* `asset` - Reference to a `RiveFileAsset` object. You'll use this reference to set a new Rive-specific asset for dynamically loaded content. If you wish to dynamically swap a given image/font over the lifetime of your view, you may want to cache this object. You can grab a number of properties from this object, such as:
* `name()` - Name of the asset without the unique file identifier appended, (i.e. `picture.webp` instead of `picture-47982.webp`)
* `uniqueFilename()` - Name of the asset with the unique file identifier, (i.e. `picture-47982.webp` instead of `picture.webp`)
* `fileExtension()` - Name of the file extension (i.e. `"png"`)
* `cdnBaseUrl()` - Name of the base URL for the CDN
* `cdnUuid()` - Identifier for the resource in the Rive CDN. Useful to see if this has length so you can see if the asset is marked for grabbing from a Rive CDN (in which case, you can let the Rive runtime retrieve the asset, rather than your app logic)
* `data` - Array of bytes for the asset. This is useful to determine if the asset is already embedded in the Rive file (aka, not marked as "referenced" in the editor)
* `factory` - Utility with methods to transform an asset's bytes into a `RiveRenderImage` ,`RiveFont`, or `RiveAudio` which the `asset` object uses to render via `.renderImage(your-rive-render-image)` , `.font(your-rive-font)` , or `.audio(your-rive-audio)` . These assets are created by calling `factory.decodeImage(data)`, `factory.decodeFont(data)`, or `factory.decodeAudio(data)`
**Important**: Note that the return value of the callback is a `boolean`, which is where you need to return:
* `true` if you intend on handling and loading in an asset yourself, or
* `false` if you do not want to handle asset loading for that given asset yourself, and attempt to have the runtime try to load the asset.
**Example Usage**
```swift theme={null}
import SwiftUI
import RiveRuntime
struct SimpleAssetReplacement: View {
@StateObject private var riveInstance = RiveViewModel(fileName: "simple_assets", autoPlay: false, loadCdn: false, customLoader: { (asset: RiveFileAsset, data: Data, factory: RiveFactory) -> Bool in
if (asset is RiveImageAsset) {
guard let url = (.main as Bundle).url(forResource: "picture-47982", withExtension: "jpeg") else {
fatalError("Failed to locate 'picture-47982' in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}
(asset as! RiveImageAsset).renderImage(
factory.decodeImage(data)
)
return true;
} else if (asset is RiveFontAsset) {
guard let url = (.main as Bundle).url(forResource: "Inter-45562", withExtension: "ttf") else {
fatalError("Failed to locate 'Inter-45562' in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(url) from bundle.")
}
(asset as! RiveFontAsset).font(
factory.decodeFont(data)
)
return true;
}
return false;
})
var body: some View {
riveInstance.view()
}
}
```
### Fonts
When using a custom loader, referenced fonts can be loaded one of two ways: with raw data (from a file, as seen above), or with a `UIFont` / `NSFont`.\
When using `UIFont` / `NSFont`, size, weight, and width of the supplied font is ignored. The font will be used as defined in the text run, rather than being overridden by the supplied font's styling.
```swift theme={null}
import SwiftUI
import RiveRuntime
struct SimpleFontReplacement: View {
@StateObject private var riveInstance = RiveViewModel(fileName: "simple_assets", autoPlay: false, loadCdn: false, customLoader: { (asset: RiveFileAsset, data: Data, factory: RiveFactory) -> Bool in
if (asset is RiveFontAsset) {
(asset as! RiveFontAsset).font(
factory.decodeFont(UIFont.systemFont(ofSize: 12))
)
return true;
}
return false;
})
var body: some View {
riveInstance.view()
}
}
```
### Images
When loading assets for referenced images, you may need to scale local assets to the size of an image asset as defined in your Rive file. When using a custom loader, you can access the size of the referenced image via the `size` property of a `RiveImageAsset`.
```swift theme={null}
import SwiftUI
import RiveRuntime
struct SimpleImageSizeReplacement: View {
@StateObject private var riveInstance = RiveViewModel(fileName: "simple_assets", autoPlay: false, loadCdn: false, customLoader: { (asset: RiveFileAsset, data: Data, factory: RiveFactory) -> Bool in
guard let imageAsset = asset as? RiveImageAsset else { return false }
let requestedSize = imageAsset.size
let image = UIImage(...)
let resizedImage = resize(image, to: requestedSize)
guard let pngData = resizedImage.pngData() else { return false }
imageAsset.renderImage(
factory.decodeImage(pngData)
)
return true
}
return false;
}
var body: some View {
riveInstance.view()
}
```
## Additional Resources
# Logging
Source: https://rive.app/docs/runtimes/apple/logging
This Rive runtime includes logging capabilities to help with debugging. These logs are *only* for debugging purposes; nothing is sent over the network, and no personally identifiable information (PII) is logged.
The table below showcases the runtimes that support logging.
The new runtime supports logging via the `RiveLog` type. To enable logging, set the `RiveLog.logger` property to a `RiveLog.Logger` implementation. The new runtime defaults to no logging, which can be set at any time by setting `RiveLog.logger` to `RiveLog.none`. The new runtime ships with a default implementation that logs to the console, which can be set by using the `RiveLog.system(levels:)` helper function. When using the system logger, any logs that are not emitted at the levels specified will be suppressed.
```swift theme={null}
// To enable the default logging implementation, which uses os.Logger
RiveLog.logger = RiveLog.system(levels: .default)
// To disable logging (the default)
RiveLog.logger = RiveLog.none
```
### Levels
Logs will be logged at various levels, which are inspired by `OSLogType` . These levels can be used to additionally filter logs to be logged at certain levels only. Available levels are:
* **Notice**: important informational logs
* **Debug**: commonly used to aid with debugging
* **Trace**: highly detailed and potentially verbose diagnostic logs, including operations like advancing that may emit at least one log per frame
* **Info**: logs that provide additional information
* **Error**: used when an error occurs
* **Warning**: used when a potential issue is detected
* **Fault**: used when a severe error occurs
* **Critical**: used when a fatal error occurs
Convenience presets are also available:
* **.default**: `.debug`, `.warning`, `.error`, `.fault`, `.critical`
* **.all**: all levels (`.notice`, `.debug`, `.trace`, `.info`, `.error`, `.warning`, `.fault`, `.critical`)
### Custom Logger
You can also implement your own custom logger by implementing the `RiveLog.Logger` protocol. This allows you to log to any desired sink, such as a file, a network endpoint, or a custom console.
```swift theme={null}
nonisolated public protocol Logger: Sendable {
nonisolated func notice(tag: Tag, _ message: @escaping () -> String)
nonisolated func debug(tag: Tag, _ message: @escaping () -> String)
nonisolated func trace(tag: Tag, _ message: @escaping () -> String)
nonisolated func info(tag: Tag, _ message: @escaping () -> String)
nonisolated func error(tag: Tag, error _: (any Error)?, _ message: @escaping () -> String)
nonisolated func warning(tag: Tag, _ message: @escaping () -> String)
nonisolated func fault(tag: Tag, _ message: @escaping () -> String)
nonisolated func critical(tag: Tag, _ message: @escaping () -> String)
}
```
Once you have implemented your own logger, you can set it to the `RiveLog.logger` property.
```swift theme={null}
RiveLog.logger = MyLogger()
```
```
RiveLogger.isEnabled = true // Enable logging; false by default
RiveLogger.levels = [.debug] // Filter logs; all by default
RiveLogger.categories = [.viewModel] // Filter categories; all by default
RiveLogger.isVerbose = true // Include verbose logs; false by default
```
# Migrating from the Legacy Rive Apple Runtime
Source: https://rive.app/docs/runtimes/apple/migrating-from-legacy
A guide to help you transition from the legacy Rive Apple runtime to the new runtime.
## Overview
The [new Rive Apple runtime](/docs/runtimes/apple/apple) is a **near-complete rewrite** of both the public API and the internal architecture. While conceptually most operations have an equivalent, the two APIs are incompatible. Any existing work in the legacy API that you would like to migrate must be rebuilt using the new API.
This guide covers:
1. [Shared Features](#shared-features) - Common operations and their equivalents.
2. [New Exclusive Features](#new-exclusive-features) - Capabilities only available in the new runtime.
3. [Legacy Exclusive Features](#legacy-exclusive-features) - Features no longer available in the new runtime and migration guidance.
This guide is not meant to be exhaustive as it would be redundant with existing general documentation. Please refer to the relevant sections of the documentation for more details on specific topics.
## Package and Import
The new Apple runtime is available in the same Swift package and CocoaPods pod as the legacy runtime. Both runtime APIs are available in the same package, so you can import the runtime using the same import statement.
```swift theme={null}
import RiveRuntime
```
## Asynchronous APIs
The new runtime is built around Swift Concurrency. Most setup and query operations are asynchronous and should be called from an async context.
For more information, see the Apple getting started guide for end-to-end setup examples.
Common examples include:
* Creating a `Worker` asynchronously
* Creating `File` and `Rive` objects
* Creating artboards, state machines, and view model instances
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
let rive = try await Rive(file: file)
```
## Lifecycles and Threading
The legacy runtime is effectively main-thread driven through `RiveViewModel` and `RiveView`.
The new runtime introduces `Worker` objects for background processing while still enforcing API calls on the main actor. In practice:
* `Worker` owns the background processing context
* `File` strongly references its `Worker`
* Out-of-band assets registered on a worker can be shared across files created with that worker
For more information, see the Threading section for additional details.
For most apps, a shared worker is recommended:
```swift theme={null}
actor WorkerProvider {
static let shared = WorkerProvider()
@MainActor
private var cachedWorker: Worker?
@MainActor
func worker() async throws -> Worker {
if let cachedWorker {
return cachedWorker
}
let worker = try await Worker()
cachedWorker = worker
return worker
}
}
```
## Shared Features
### Shared APIs
The main shared API area is fallback fonts through `RiveFont.fallbackFontsCallback`.
For fallback font types and behavior, see [Fallback Fonts](/docs/runtimes/fonts#fallback-fonts).
### RiveViewModel to Rive
Use the sections below as a migration map: [Loading a File from Disk](#loading-a-file-from-disk), [Creating a Rive View](#creating-a-rive-view), and [Data Binding](#data-binding). Legacy `RiveViewModel` flows in these areas become explicit `File` + `Rive` setup, then `RiveUIView`/SwiftUI representables in the new runtime.
### Loading a File from Disk
#### Legacy Runtime
```swift theme={null}
// Cache this file for reuse
let file = try RiveFile(name: "my_rive_file")
let model = RiveModel(riveFile: file)
let viewModel = RiveViewModel(model)
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let rive = try await Rive(file: file)
```
### Loading a File from URL
#### Legacy Runtime
```swift theme={null}
let webURL = URL(string: "https://example.com/my_rive_file.riv")!
let file = RiveFile(httpUrl: webURL, loadCdn: false, with: self)
let model = RiveModel(riveFile: file)
let viewModel = RiveViewModel(model)
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let webURL = URL(string: "https://example.com/my_rive_file.riv")!
let file = try await File(
source: .url(webURL),
worker: worker
)
let rive = try await Rive(file: file)
```
### Loading a File from Data (Bytes)
#### Legacy Runtime
```swift theme={null}
let data: Data = ...
let file = try RiveFile(data: data, loadCdn: false)
let model = RiveModel(riveFile: file)
let viewModel = RiveViewModel(model)
```
#### New Runtime
Use the `.data` file source when your `.riv` bytes are already available in memory.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let data: Data = ...
let file = try await File(source: .data(data), worker: worker)
let rive = try await Rive(file: file)
```
### Tracking Loading and Error State
For common setup operations (loading files, creating artboards, creating state machines), the migration pattern is:
* Legacy runtime: many lookup APIs return optionals (`nil` on failure), so handle with `guard let` / fallback logic.
* New runtime: APIs throw errors, so use `do/catch`.
#### Legacy Runtime
Legacy APIs commonly return `nil` for lookup/create-style operations:
```swift theme={null}
let file = try RiveFile(name: "my_rive_file")
guard let artboard = file.artboard() else {
// Handle missing artboard
return
}
guard let stateMachine = artboard.defaultStateMachine() else {
// Handle missing state machine
return
}
```
#### New Runtime
New runtime APIs throw errors, so failure handling moves to `do/catch`:
```swift theme={null}
do {
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let artboard = try await file.createArtboard("My Artboard")
let stateMachine = try await artboard.createStateMachine("My State Machine")
let rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine)
} catch {
// Handle file/artboard/state machine setup errors
}
```
This pattern applies equally in UIKit and SwiftUI. The key migration change is optional unwrapping in legacy vs error catching in the new runtime.
### Choosing an Artboard and State Machine
For more information, see artboards documentation for more details.
For more information, see state machines documentation for more details.
#### Legacy Runtime
```swift theme={null}
let file = try RiveFile(name: "my_rive_file")
let model = RiveModel(riveFile: file)
model.setArtboard("My Artboard")
model.setStateMachine("My State Machine")
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let artboard = try await file.createArtboard("My Artboard")
let stateMachine = try await artboard.createStateMachine("My State Machine")
let rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine)
```
### Creating a Rive View
#### Legacy Runtime
```swift theme={null}
let viewModel = RiveViewModel(...)
// UIKit
let riveView = viewModel.createRiveView()
// SwiftUI
var body: some View {
viewModel.view()
}
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
// UIKit (sync object already available)
let riveView = RiveUIView(rive: rive)
// UIKit (async loading)
let riveView = RiveUIView({
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
return try await Rive(file: file)
})
// SwiftUI
var body: some View {
RiveUIViewRepresentable(rive)
}
// SwiftUI (async)
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
return try await Rive(file: file)
}
}
```
### Setting Fit and Alignment
For more information, see the layout docs for all fit and alignment options.
#### Legacy Runtime
```swift theme={null}
let viewModel = RiveViewModel(
fileName: "my_rive_file",
fit: .contain,
alignment: .center
)
// Update at runtime
viewModel.fit = .fitWidth
viewModel.alignment = .topCenter
```
To use artboard layout sizing in the legacy runtime:
```swift theme={null}
let viewModel = RiveViewModel(fileName: "my_rive_file")
viewModel.fit = .layout
viewModel.layoutScaleFactor = RiveViewModel.layoutScaleFactorAutomatic // default behavior
// Or explicitly set a scale factor
viewModel.layoutScaleFactor = 2.0
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
var rive = try await Rive(file: file, fit: .contain(alignment: .center))
// Update at runtime
rive.fit = .fitWidth(alignment: .topCenter)
```
To use artboard layout sizing in the new runtime:
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
var rive = try await Rive(file: file, fit: .layout(scaleFactor: .automatic))
// Or explicitly set a scale factor
rive.fit = .layout(scaleFactor: .explicit(2.0))
```
### Default Layout Scale Factor
When using layout fit, both runtimes support automatic and explicit scale factors.
#### Legacy Runtime
Use `RiveViewModel.layoutScaleFactorAutomatic` (default) or set an explicit numeric value on `layoutScaleFactor`.
#### New Runtime
Use `.layout(scaleFactor: .automatic)` or `.layout(scaleFactor: .explicit(...))` on `Rive.fit`.
### View Models
For more information, see the data binding docs for complete view model instance APIs.
#### Legacy Runtime
In legacy, view models are queried from `RiveFile`, then instances are created from that queried view model.
```swift theme={null}
let riveViewModel = RiveViewModel(...)
let file = riveViewModel.riveModel!.riveFile
guard let viewModel = file.viewModelNamed("My View Model") else {
return
}
// Create blank/default/named instances from the queried view model
let blankInstance = viewModel.createInstance()
let defaultInstance = viewModel.createDefaultInstance()
let namedInstance = viewModel.createInstance(fromName: "My Instance")
```
#### New Runtime
In the new runtime, the view model is represented as source metadata passed directly into `createViewModelInstance`.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
// By explicit view model name
let blankInstance = try await file.createViewModelInstance(
from: .blank(from: .name("My View Model"))
)
let defaultInstance = try await file.createViewModelInstance(
from: .name("My View Model")
)
let namedInstance = try await file.createViewModelInstance(
from: .name("My Instance", from: .name("My View Model"))
)
```
You can also source the view model from an artboard default:
```swift theme={null}
let artboard = try await file.createArtboard()
let defaultFromArtboard = try await file.createViewModelInstance(
from: .viewModelDefault(from: .artboardDefault(artboard))
)
```
### View Model Instance Properties
#### Legacy Runtime
Legacy data-binding instances expose typed property objects that you query from the instance.
```swift theme={null}
let riveViewModel = RiveViewModel(...)
var instance: RiveDataBindingViewModel.Instance!
riveViewModel.riveModel?.enableAutoBind { boundInstance in
instance = boundInstance
}
guard let stringProperty = instance.stringProperty(fromPath: "path/to/string") else {
return
}
// Set
stringProperty.value = "Hello, Rive"
// Get
let currentValue = stringProperty.value
```
#### New Runtime
The new runtime uses typed path descriptors with methods on `ViewModelInstance` for set/get/observe.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let viewModelInstance = try await file.createViewModelInstance(
from: .name("My View Model")
)
let stringProperty = StringProperty(path: "path/to/string")
// Set
viewModelInstance.setValue(of: stringProperty, to: "Hello, Rive")
// Get current value
let currentValue = try await viewModelInstance.value(of: stringProperty)
// Observe changes over time
let valueStream = viewModelInstance.valueStream(of: stringProperty)
for try await updatedValue in valueStream {
print(updatedValue)
}
```
### Bindable Artboards
#### Legacy Runtime
Legacy artboard property binding uses a bindable artboard wrapper type.
```swift theme={null}
let instance: RiveDataBindingViewModel.Instance = ...
guard let artboardProperty = instance.artboardProperty(fromPath: "path/to/artboard") else {
return
}
let components = try RiveFile(name: "component_library")
guard let bindableArtboard = components.bindableArtboard(withName: "My Artboard") else {
return
}
artboardProperty.setValue(bindableArtboard)
```
#### New Runtime
The new runtime binds artboards with a typed property descriptor and `setValue` on the instance.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let viewModelInstance = try await file.createViewModelInstance(.viewModelDefault(from: .name("My View Model")))
let artboardProperty = ArtboardProperty(path: "path/to/artboard")
let artboard = try await file.createArtboard("My Artboard")
viewModelInstance.setValue(of: artboardProperty, to: artboard)
```
### Data Binding
For more information, see the data binding docs for full API coverage.
#### Legacy Runtime
```swift theme={null}
let file = try RiveFile(...)
let model = RiveModel(riveFile: file)
model.enableAutoBind { instance in
self.viewModelInstance = instance
instance.stringProperty(fromPath: "...").value = "Hello, Rive"
}
```
You can also manually bind a specific instance:
```swift theme={null}
let file = try RiveFile(...)
guard let artboard = file.artboard() else { return }
guard let stateMachine = artboard.defaultStateMachine() else { return }
guard let viewModel = file.defaultViewModel(for: artboard) else { return }
guard let instance = viewModel.createDefaultInstance() else { return }
stateMachine.bindViewModelInstance(instance) // bound successfully
```
#### New Runtime
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: ..., worker: worker)
// Auto bind (default)
let autoBoundRive = try await Rive(file: file, dataBind: .auto)
// Bind a specific instance
let artboard = try await file.createArtboard()
let viewModel = try await file.createViewModelInstance(
.viewModelDefault(from: .artboardDefault(artboard))
)
let instanceBoundRive = try await Rive(file: file, dataBind: .instance(viewModel))
// Opt out of data binding
let noBindingRive = try await Rive(file: file, dataBind: .none)
```
### Updating a Data Bind Unsettles the State Machine
Legacy and new runtime behavior differ here:
* Legacy: after changing a data bound property, you typically call `play()` to advance/unsettle and apply the change if the graphic has settled.
* New runtime: setting a data bound property no longer requires an explicit `play()` call, simplifying the API.
Best practice for migration: if you need initial values, set them **before** creating and presenting a view.
This avoids showing default values for a frame before your app-provided values are applied.
#### Legacy Runtime
When updating a bound value after the view is created, keep a reference to the instance and call `play()` after mutation:
```swift theme={null}
let viewModel = RiveViewModel(...)
var instance: RiveDataBindingViewModel.Instance?
viewModel.riveModel?.enableAutoBind { boundInstance in
instance = boundInstance
}
let riveView = viewModel.createRiveView()
instance?.stringProperty(fromPath: "path/to/string")?.value = "Updated Value"
viewModel.play() // Required to advance/unsettle after mutation
```
#### New Runtime
In the new runtime, update the bound value after the view is created. No explicit `play()` call is needed.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let instance = try await file.createViewModelInstance(.viewModelDefault(from: .name("My View Model")))
let property = StringProperty(path: "path/to/string")
let rive = try await Rive(file: file, dataBind: .instance(instance))
let riveView = RiveUIView(rive: rive)
instance.setValue(of: property, to: "Updated Value") // Applies without play()
```
### Playing and Pausing
For more information, see playback controls in the Apple runtime guide.
#### Legacy Runtime
```swift theme={null}
let viewModel = RiveViewModel(fileName: "...")
viewModel.pause() // or play() to resume
```
#### New Runtime
```swift theme={null}
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
riveView.isPaused = true // or false to resume
// SwiftUI
RiveUIViewRepresentable(rive)
.paused(true)
```
### Frame Rate
For more information, see frame rate controls and ProMotion notes.
#### Legacy Runtime
```swift theme={null}
let viewModel = RiveViewModel(fileName: "...")
viewModel.setPreferredFramesPerSecond(preferredFramesPerSecond: 30)
```
#### New Runtime
```swift theme={null}
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
riveView.frameRate = .fps(30)
// or
riveView.frameRate = .range(minimum: 30, maximum: 60)
// or
riveView.frameRate = .default
```
### Loading Referenced Assets
#### Legacy Runtime
The legacy runtime uses a pull model via a callback that resolves assets on demand.
```swift theme={null}
let viewModel = RiveViewModel(fileName: "my_rive_file") { asset, _, factory in
if let imageAsset = asset as? RiveImageAsset {
let decodedImage = factory.decodeImage(Data(...))
imageAsset.renderImage(decodedImage)
return true
} else if let fontAsset = asset as? RiveFontAsset {
let decodedFont = factory.decodeFont(Data(...))
fontAsset.font(decodedFont)
return true
} else if let audioAsset = asset as? RiveAudioAsset {
let decodedAudio = factory.decodeAudio(Data(...))
audioAsset.audio(decodedAudio)
return true
} else {
return false
}
}
```
#### New Runtime
The new runtime uses a push model, where assets are registered on the worker ahead of use.
```swift theme={null}
let worker = try await WorkerProvider.shared.worker()
let image = try await worker.decodeImage(from: Data(...))
worker.addGlobalImageAsset(image, name: "MyImage-1234")
let font = try await worker.decodeFont(from: Data(...))
worker.addGlobalFontAsset(font, name: "MyFont-1234")
let audio = try await worker.decodeAudio(from: Data(...))
worker.addGlobalAudioAsset(audio, name: "MyAudio-1234")
```
### Logging
For more information, see the logging guide for shared concepts and filtering options.
#### Legacy Runtime
```swift theme={null}
RiveLogger.isEnabled = true
RiveLogger.levels = [.debug, .error]
RiveLogger.categories = [.stateMachine, .artboard, .viewModel]
RiveLogger.isVerbose = true
```
#### New Runtime
```swift theme={null}
RiveLog.logger = RiveLog.system(levels: .default)
// Or use your own implementation of RiveLog.Logger
RiveLog.logger = MyLogger()
// Disable logs
RiveLog.logger = RiveLog.none
```
### Fallback Fonts
Both runtimes support fallback fonts.
* `RiveFont.fallbackFontsCallback` remains available
* Existing fallback font strategies can be reused across both runtimes
```swift theme={null}
RiveFont.fallbackFontsCallback = { style in
switch style.weight {
case .thin:
return [RiveFallbackFontDescriptor(weight: .thin)]
case .bold:
return [RiveFallbackFontDescriptor(weight: .bold)]
default:
return [RiveFallbackFontDescriptor()]
}
}
```
## New Exclusive Features
### Worker-Based Concurrency
The new runtime introduces `Worker` as an explicit concurrency primitive. This is a substantial change from the legacy runtime, which did not expose an equivalent concept.
Benefits include:
* Better control over background processing
* Shared global asset registration per worker
* More predictable architecture when rendering multiple files
A shared worker is usually the best default. Use multiple workers only when you need additional parallel processing, such as when rendering multiple heavyweight graphics.
### Async Initialization APIs
The new runtime supports async constructors and async view wrappers (`RiveUIView` with async closure and `AsyncRiveUIViewRepresentable`) to better model real-world loading flows.
The async wrappers are useful when a view must own its own loading lifecycle. If you need reuse and caching across screens, prefer creating and storing `File`/`Rive` objects at a higher level.
## Legacy Exclusive Features
Some features in the legacy runtime are intentionally not present in the new runtime.
### CDN Assets
Legacy file loading may use CDN-backed asset flows (for example via `loadCdn` behavior). The new runtime's asset model is worker-based and push-oriented via explicit asset registration APIs.
### State Machine Inputs
The legacy runtime supports state machine input APIs directly. The new runtime does not expose equivalent input APIs, and migration should move to data binding properties (number, boolean, string, trigger-style interactions).
The Rive Editor provides a conversion tool in **Menu > Convert Inputs to ViewModels** that can help with initial migration.
There are no current plans to reintroduce direct state machine input APIs in the new runtime.
### Events
The legacy runtime supports event listeners. The new runtime does not currently expose an equivalent event listener API. For many migration scenarios, a data binding contract is the recommended replacement for app-runtime communication.
Simple event-like behavior can often be modeled with trigger-oriented view model properties.
There are no current plans to reintroduce legacy-style event listener APIs in the new runtime.
### Linear Animations
Legacy integrations can directly target linear animations. In migration, graphics are required to contain a state machine.
For existing files that rely on linear animations, create a state machine in the Editor with a single state that plays the desired animation.
### Observing State Machine State
Legacy integrations often used state-change delegate callbacks (for example, `RiveStateDelegate.stateChange(...)`) to react to animation state.
The new runtime does not expose a direct state-name observation API in this guide. For migration, model those app-facing signals as data-binding properties and observe them from the bound instance.
```swift theme={null}
let property = StringProperty(path: "path/to/state_signal")
let stream = viewModelInstance.valueStream(of: property)
for try await value in stream {
// React to state-like changes emitted from the Rive file
}
```
### Getting by Index
Where possible, prefer named queries and explicit sources (for example, `ViewModelSource` and named artboard/state machine queries) over index-based coupling.
# Migration Guides
Source: https://rive.app/docs/runtimes/apple/migration-guides
Migrating between major versions of the Apple runtime
Contains a breaking change
## Rive Renderer
### RendererType
`riveRenderer` is now the new default renderer type, and `skiaRenderer` has been removed. If you were previously explicitly setting the renderer type to Skia, then you will have to [specify a new renderer](/docs/runtimes/choose-a-renderer/), or use the new default Rive Renderer (our recommendation).
## Package Size
Rive's iOS runtime is now \~57% smaller, at \~3.3mb, compared to \~7.6mb prior to v6.0.0.
No breaking API changes!
There should be no major changes to your code to migrate to `v5.x.x`. Starting in `v5.x.x`, a new text engine dependency to support the new Rive [Text](/docs/runtimes/apple/text) feature, so you may see a slight bump in the size of the package to account for this.
There should be no major changes to your code to migrate to `v4.x.x`. Starting in `v4.0.1`, you can use the same runtime package `rive-ios` to install `RiveRuntime` into native `macOS` applications. The API usage of `RiveRuntime` in iOS and macOS applications should remain the same. If you do find any discrepancies or issues, please log an issue to [https://github.com/rive-app/rive-ios/issues](https://github.com/rive-app/rive-ios/issues).
[https://github.com/rive-app/rive-ios/issues](https://github.com/rive-app/rive-ios/issues)
Migrating to v3 of the Apple runtime should be fairly straightforward. See the sections below on concerns you may need to look out for to change to your app if you upgrade.
## Enums Naming
Starting in v3, the Layout option enums have changed to match enum naming conventions from the other runtimes for consistency. See below for what you should change `Fit` and `Alignment` options to. There are also a few other enums for parameters that have changed slightly regarding loop modes and direction.
| Fit | Before | After |
| ---------- | --------------- | ------------ |
| Fill | `.fitFill` | `.fill` |
| Contain | `.fitContain` | `.contain` |
| Cover | `.fitCover` | `.cover` |
| Fit Width | `.fitFitWidth` | `.fitWidth` |
| Fit Height | `.fitFitHeight` | `.fitHeight` |
| Scale Down | `.fitScaleDown` | `.scaleDown` |
| None | `.fitNone` | `.noFit` |
| Alignment | Before | After |
| ------------- | ------------------------ | --------------- |
| Top Left | `.alignmentTopLeft` | `.topLeft` |
| Top Center | `.alignmentTopCenter` | `.topCenter` |
| Top Right | `.alignmentTopRight` | `.topRight` |
| Center Left | `.alignmentCenterLeft` | `.centerLeft` |
| Center | `.alignmentCenter` | `.center` |
| Center Right | `.alignmentCenterRight` | `.centerRight` |
| Bottom Left | `.alignmentBottomLeft` | `.bottomLeft` |
| Bottom Center | `.alignmentBottomCenter` | `.bottomCenter` |
| Bottom Right | `.alignmentBottomRight` | `.bottomRight` |
| Loop Mode | Before | After |
| --------- | -------------- | ---------- |
| One Shot | `loopOneShot` | `oneShot` |
| Loop | `loopLoop` | `loop` |
| Ping Pong | `loopPingPong` | `pingPong` |
| Auto | `loopAuto` | `autoLoop` |
| Direction | Before | After |
| --------- | -------------------- | --------------- |
| Backwards | `directionBackwards` | `backwards` |
| Forwards | `directionForwards` | `forwards` |
| Auto | `directionAuto` | `autoDirection` |
## Default playing behavior
One changed default behavior in v3 is what plays in the Rive canvas. Before v3, if no state machine or specific animation was specified when setting up the `RiveViewModel`, the first animation made in the Rive file would play.
With v3, if no state machine or specific animation is specified, the first state machine (if one is created) in the Rive file will play. So if you prefer to keep your existing default behavior of the first animation, simply set the `animationName` property on `RiveViewModel` when creating it.
The Rive Apple runtime has a different API in 2.x.x from 1.x.x that allows for a unified internal model that supports both Storyboard/UIKit and SwiftUI usage.
There are now 3 main pieces of the Rive API to be familiar with for iOS development:
* `RiveView` - Core logic for building and manipulating Rive views
* `RiveModel` - Describes the configuration model for Rive objects
* `RiveViewModel` - The main class to interface with when integrating rive, creating a Rive view in some instances. It provides a high-level API that makes it simple to do actions like instantiation, animation playback, layout changes, and more.
We recommend migrating to the latest version of v2.x.x as soon as possible, and you can find steps on this below:
## UIKit
In v1.x.x, you may have loaded in a Rive file in the following snippet pattern:
```javascript theme={null}
class SimpleAnimationViewController: UIViewController {
let url = "https://cdn.rive.app/animations/truck.riv"
override public func loadView() {
super.loadView()
let view = RiveView()
guard let riveFile = RiveFile(httpUrl: url, with: view) else {
fatalError("Unable to load RiveFile")
}
try? view.configure(riveFile)
self.view = view
}
}
```
This pattern interfaced with 2 Rive APIs, `RiveFile` and `RiveView`. With v2.x.x, the pattern becomes simpler, interfacing with one `RiveViewModel`.
```javascript theme={null}
class SimpleAnimationViewController: UIViewController {
var viewModel = RiveViewModel(fileName: "truck")
override func viewWillAppear(_ animated: Bool) {
let riveView = viewModel.createRiveView()
view.addSubview(riveView)
riveView.frame = view.frame
}
}
```
Here's another example:
```javascript theme={null}
class MultipleAnimationsController: UIViewController, RivePLayerDelegate {
@IBOutlet weak var riveView: RiveView!
var viewModel = RiveViewModel(
fileName: "multiple_animations",
animationName: "Animation 1",
artboardName: "Animation Playground"
)
override func viewDidLoad() {
viewModel.setView(riveView)
}
}
```
See subsequent runtime pages for new usage of animation playback and layouts with UIKit.
### State Machine Usage
In v1.x.x, you would set state machine input values with the following API: `riveView.setNumberState("Number Test", inputName: "Level", value: 2.0)`
`riveView.setBooleanState("Boolean Test", inputName: "isSuccess", value: true)`
`riveView.fireState("Trigger Test", inputName: "trigFail")`
In v2.x.x, some of the input state setters have been consolidated and renamed. Additionally, the setters are called on the `RiveViewModel` which has the context of the state machine that was instantiated, so there is no longer a need to pass it the name: viewModel`.setInput("Level", value: 2.0)`
viewModel`.setInput("isSuccess", value: true)`
`viewModel.triggerInput("trigFail")`
### Delegates
In the past, you may have implemented various functions that came with some of the following delegates: `LoopDelegate` , `PlayDelegate`, `PauseDelegate`, `StopDelegate`, and `StateChangeDelegate`. The various functions that get implemented on your end (i.e `loop`, `play`, `pause`, `stateChange`, etc.) have been consolidated under 2 main delegates, `RivePlayerDelegate` and `RiveStateMachineDelegate` with a slightly different function to override.
See the following list of delegates for methods to hook into:
* `RivePlayerDelegate` - Hook into animation and state machine lifecycle events
* `player`: `(loopedWithModel riveModel: RiveModel?, type: Int) {}`
* `player`: `(playedWithModel riveModel: RiveModel?) {}`
* `player`: `(pausedWithModel riveModel: RiveModel?) {}`
* `player`: `(stoppedWithModel riveModel: RiveModel?) {}`
* `RiveStateDelegate` - Hook into state changes on a state machine lifecycle
* `stateChange`: `(_ stateMachineName: String, _ stateName: String) {}`
## SwiftUI
v1.x.x had a small wrapper around the existing `RiveView` class to help support Rive in the context of applications written in SwiftUI. v2.x.x now supports a more robust pattern for consuming Rive in your SwiftUI applications that fixes several bugs with the existing wrapper approach and provides a closer experience with the new pattern of SwiftUI.
See subsequent runtime pages to learn how to control animation playback, state machines, and more with v2.x.x
```javascript theme={null}
struct AnimationView: View {
var body: some View {
RiveViewModel(fileName: "cool_rive_animation").view()
}
}
```
# Playing Audio
Source: https://rive.app/docs/runtimes/apple/playing-audio
Playing Rive audio events
To learn more on how to add audio to your Rive file, see [Audio Events](/docs/editor/events/audio-events).
## Embedded Assets
Embedded assets require no additional work to play audio.
## Referenced Assets
Referenced assets require a little bit more work to play audio. Audio will still automatically play, but the audio file(s) must be loaded when a Rive runtime attempts to play audio.
For more information, see [Loading Assets](/docs/runtimes/apple/loading-assets).
## Audio Settings
On iOS, playing audio will respect your `AVAudioSession` shared instance settings. For more information, see [Apple's documentation](https://developer.apple.com/documentation/avfaudio/avaudiosession) on `AVAudioSession`. Using this, you can choose to mix audio, duck audio, and more. You can update your shared instance early in your app lifecycle if you would like to ensure all Rive audio plays with the correct settings.
```swift IOS theme={null}
// Example: Ignore the silent switch, and mix with other audio
let category: AVAudioSession.Category = .playback
let options: AVAudioSession.CategoryOptions = [.mixWithOthers]
AVAudioSession.sharedInstance().setCategory(category, options: options)
```
## Setting Volume
An artboard is capable of setting its volume. A parent artboard will set the volume of all component instances; however, setting a component's volume will **not** update the parent's volume.
Once you have created an `Artboard`, you can set its volume with `setVolume(_:)` and read the current volume with `volume()`. Volume is propagated to all nested artboards.
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: .local("my_rive_file", Bundle.main), worker: worker)
let artboard = try await file.createArtboard()
// `setVolume(_:)` is a @MainActor method on `Artboard`.
// Set the artboard's volume to 50% (0.0 = muted, 1.0 = full volume)
artboard.setVolume(0.5)
// `volume()` is a @MainActor async throwing method that throws
// an `ArtboardError` if the request fails.
do {
let volume = try await artboard.volume()
print("Current volume: \(volume)")
} catch let error as ArtboardError {
print("Failed to read volume: \(error)")
}
```
```swift theme={null}
// Set the current artboard's volume to 50%
let viewModel = RiveViewModel(fileName: "my_rive_file")
viewModel.riveModel?.volume = 0.5
```
# Resource Usage
Source: https://rive.app/docs/runtimes/apple/resource-usage
This page outlines some additional considerations when comparing Rive to other libraries' resource usage (specifically CPU and memory).
An important note is that Rive uses Metal APIs directly over other APIs and frameworks (like Core Animation) to be able to adjust its usage for best performance with Rive.
To get an accurate representation of the overall CPU and memory used by Rive, consider using the "Activity Monitor" template in Xcode, in addition to other templates.
Since Rive uses Metal directly, CPU usage and memory allocations appear in the app process. Other APIs can make use of other system processes, the stats of which will not be immediately visible by Xcode or Instruments.
## Core Animation
Lottie is an example of a library that uses Core Animation.
For libraries using Core Animation, logic and rendering is managed in a separate process known as the "Render Server" (`backboardd`). In doing so, CPU and memory usage isn't reported by the app process itself, and instead is reported by `backboardd`, which Xcode and Instruments are not monitoring by default.
By default, Xcode and Instruments show stats for the single process it is monitoring (and attached to). This is commonly the app that you are developing, unless otherwise specified. Resource usage for libraries using Core Animation will additionally appear in the "Render Server" process `backboardd`, and not just the app process.
The **overall** difference in resource usage can be found when profiling your app process *in addition to* the `backboardd` process. This can be seen by using the “Activity Monitor” Instruments template, and filtering by your app and the `backboardd` processes.
# Semantics
Source: https://rive.app/docs/runtimes/apple/semantics
Make your Rive views accessible to VoiceOver by enabling semantics in the Apple runtime.
Semantics is currently only available in [Early Access](https://rive.app/downloads?utm_source=docs\&utm_medium=content). Runtime support is in development or released as experimental.
Have feedback? Join the [Early Access community](https://community.rive.app/c/early-access/) to share your thoughts and help shape the feature.
This page covers enabling [semantics](/docs/editor/accessibility/semantics) in the New Runtime so your Rive views are accessible to VoiceOver. To learn what semantics are and how to add them to a graphic, see the [Semantics](/docs/editor/accessibility/semantics) editor documentation.
## Overview
In the Rive Editor, you can add semantic meaning to certain elements of your graphic-roles such as button, checkbox, tab, image, list, dialog, and more. Alongside these roles, you can add associated labels, values, states, and actions. These settings vary per role.
At runtime, the New Runtime reads those semantics from the running state machine and exposes them to [VoiceOver](https://support.apple.com/guide/iphone/turn-on-and-practice-voiceover-iph3e2e415f/ios) as the view's `accessibilityElements`, keeping them up to date as the state machine advances.
Semantics are **opt‑in**. The default mode is `.off`, so no accessibility elements are created until you enable semantics on the view.
Semantics must be defined in the editor to have any effect. If an element has no semantics, it is not exposed to screen readers, regardless of the mode you set. See [Feature Support](/docs/feature-support) for which runtimes currently support semantics.
## Availability
Semantics are available in the New Runtime only (not the Legacy Runtime), on every Apple platform the runtime supports except macOS (AppKit):
| Platform | Supported |
| -------------- | --------- |
| iOS / iPadOS | ✅ |
| tvOS | ✅ |
| visionOS | ✅ |
| Mac Catalyst | ✅ |
| macOS (AppKit) | ❌ |
## Semantics modes
Semantics are controlled by the `Semantics` enum, which sets the VoiceOver integration mode for a Rive view:
| Mode | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `.off` | **Default.** Accessibility semantics are disabled. No accessibility elements are created, regardless of VoiceOver state. |
| `.on` | Accessibility semantics are always active. Elements are created and kept up‑to‑date on every frame. |
| `.automatic` | Accessibility semantics activate and deactivate automatically based on whether VoiceOver is currently running. |
For most apps, prefer `.automatic` — it keeps the accessibility tree in sync only while VoiceOver is active, avoiding unnecessary work when it isn't.
## Usage
Set the semantics mode on the view (UIKit) or with the `.semantics(_:)` modifier (SwiftUI).
```swift UIKit theme={null}
let rive = try await Rive(...)
let riveView = RiveUIView(rive: rive)
// Enable semantics only while VoiceOver is running
riveView.semantics = .automatic
// Always keep semantics active
riveView.semantics = .on
// Disable semantics (default)
riveView.semantics = .off
```
```swift SwiftUI theme={null}
var body: some View {
RiveUIViewRepresentable(rive)
.semantics(.automatic)
// or .semantics(.on)
// or .semantics(.off)
}
```
```swift SwiftUI (Async) theme={null}
var body: some View {
AsyncRiveUIViewRepresentable {
let worker = try await Worker()
let file = try await File(source: ..., worker: worker)
let rive = try await Rive(file: file)
return rive
}
.semantics(.automatic)
}
```
# State Machine Playback
Source: https://rive.app/docs/runtimes/apple/state-machines
Playing a state machine
For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/docs/editor/state-machine).
A Rive state machine is a set of animation states and the transitions between them. At runtime there is limited ability to observe or modify the state directly. This is by design, as this would limit the ability of a designer in Rive to modify the state machine without creating breaking changes. Instead, state machines are indirectly controlled through transitions conditioned on Data Binding properties.
A designer assigns a default state machine for each artboard in the Rive editor. They may create multiple state machines, each representing a different configuration of states and transitions. When rendering a Rive file and artboard, you may choose which state machine to play. If no state machine is specified, the default state machine for that artboard is used.
## Controlling Playback
State machines play by "advancing" over time. This is done once per frame by the amount of time between frames. For example, for a graphic running at 60 frames per second, the state machine would be advanced by approximately 16.67 milliseconds (1/60th of a second) each frame. This advancing evaluates keyframes, transitions, data bindings changes, and ultimately the visible artboard elements to create the illusion of motion over time.
This runtime provides a way to control whether the state machine is playing. When paused or stopped, the state machine does not advance and the last rendered frame remains visible. When playing from pause, the state machine resumes from where it left off, whereas when playing from stop, it restarts from the entry state.
In addition to the paused/stopped state, state machines may also "settle". This is an optimization where the Rive runtime detects that no further changes will occur (for example, if there are no active transitions or animations). While settled the state machine will also stop advancing. This improves performance and energy use by avoiding unnecessary calculations. State machines are unsettled by external actions that change their state, such as user input or data binding changes. You can additionally force a state machine to unsettle by calling play, though it may immediately re-settle if there is no further work to be done.
## Playing State Machines
By default, the state machine of a `Rive` object will automatically play when in use by a `RiveUIView`.
The following sections assumes that you have read through the [Getting an Artboard](/docs/runtimes/apple/artboards#getting-an-artboard) overview.
### Getting a State Machine
Once you have created a `File` and `Artboard`, you can then retrieve information for and create `StateMachine` types.
```swift theme={null}
// Get all state machine names
let stateMachineNames = try await artboard.getStateMachineNames()
// Get the default state machine for the artboard
let defaultStateMachine = try await artboard.createStateMachine()
// Get a state machine by name from the artboard
let stateMachineByName = try await artboard.createStateMachine("StateMachine")
```
Note that these are all async throwing functions marked as `@MainActor`. Since they are functions called on an `Artboard` object, any thrown errors will be of type `ArtboardError`.
An example of when one of these functions will throw is if you call `.createStateMachine(_:)` with a name that is not in the origin `Artboard`, which will throw a `ArtboardError.invalidStateMachine(String)`.
### Using a State Machine
Remember that the Rive configuration for a view is the `Rive` type. In the overview, we show initializing a `Rive` object with just a file. However, you can initialize a `Rive` object with a specific state machine:
```swift theme={null}
let worker = try await Worker()
let file = try await File(source: .local("my_file", Bundle.main), worker: worker)
let artboardByName = try await file.createArtboard("Artboard")
let stateMachine = try await artboardByName.createStateMachine("StateMachine")
let rive = try await Rive(file: file, artboard: artboardByName, stateMachine: stateMachine)
```
#### Autoplay the State Machine
By default, RiveViewModel will automatically play the given state machine.
### SwiftUI
```swift theme={null}
var stateChanger = RiveViewModel(
fileName: "skills",
stateMachineName: "Designer's Test",
artboardName: "Banana"
)
```
### UIKit
```swift theme={null}
class StateMachineViewController: UIViewController {
var viewModel = RiveViewModel(
fileName: "skills",
stateMachineName: "Designer's Test",
artboardName: "Banana"
)
override public func loadView() {
super.loadView()
guard let stateMachineView = view as? StateMachineView else {
fatalError("Could not find StateMachineView")
}
viewModel.setView(stateMachineView.riveView)
}
}
```
### Play
If you set autoplay to false you can simply play the active animation or state machine.
```swift theme={null}
simpleVM.play()
```
### Pause/Stop/Reset
Based on certain events in your app you may want to adjust the playback further.
```swift theme={null}
simpleVM.pause()
simpleVM.stop()
simpleVM.reset()
```
# Choosing a Renderer
Source: https://rive.app/docs/runtimes/choose-a-renderer/overview
Choose and configure the renderer used by Rive runtimes.
Rive runtimes can render through the [Rive Renderer](https://rive.app/renderer?utm_source=docs\&utm_medium=content) or, in some runtimes, a platform-specific renderer such as Canvas2D, Skia, or Impeller.
The Rive Renderer is designed to provide better performance and visual fidelity across all platforms. It leverages modern graphics APIs and techniques to deliver high-quality rendering for Rive graphics.
Some Rive features, including Vector Feathering, require the Rive Renderer.
See [Feature
Support](/docs/feature-support) for more information.
## Renderer Defaults
| Runtime | Default Renderer | Options |
| ----------------------------------- | ------------------ | -------------------------------------------- |
| [Web (JS)](#web-js) | Depends on package | Rive / Canvas2D |
| [Web (React)](#web-react) | Depends on package | Rive / Canvas2D |
| Apple | Rive | Rive |
| Android (Compose) | Rive | Rive |
| [Android (Legacy)](#android-legacy) | Rive | Rive / Canvas / Skia (removed as of v10.0.0) |
| React Native | Rive | Rive |
| [Flutter](#flutter) | No default | Rive / Flutter Skia / Flutter Impeller |
## Runtime Setup
Only some runtimes require you to choose or configure a renderer. The sections below cover runtimes that expose renderer setup through packages, initialization options, or factory settings.
If a runtime is not listed here, there is no renderer setup to configure. It uses the renderer supported by that runtime automatically.
The Web runtime provides multiple packages with the same core API. Choose the package based on the renderer you want to use.
| Package | Renderer | Use when |
| ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `@rive-app/webgl2` | Rive | You want the best support for Rive Renderer features and rendering fidelity. |
| `@rive-app/canvas` | Canvas2D | You have several Rive instances on screen, want a slightly smaller package, or do not need Rive Renderer-only features. |
| `@rive-app/canvas-lite` | Canvas2D | You want the smallest Canvas2D package and do not need the full Canvas2D runtime feature set. |
The Web runtime provides multiple packages with the same core API. Choose the package based on the renderer you want to use.
| Package | Renderer | Use when |
| ----------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `@rive-app/react-webgl2` | Rive Renderer | You want the best support for Rive Renderer features and rendering fidelity. |
| `@rive-app/react-canvas` | Canvas2D | You have several Rive instances on screen, want a slightly smaller package, or do not need Rive Renderer-only features. |
| `@rive-app/react-canvas-lite` | Canvas2D | You want the smallest Canvas2D package and do not need the full Canvas2D runtime feature set. |
These packages provide the Rive React component and hooks. If you want to use the imperative JavaScript runtime in a React app, install one of the Web JavaScript packages instead, such as `@rive-app/webgl2`, `@rive-app/canvas`, or `@rive-app/canvas-lite`.
Options: `Canvas / Skia (removed in v10.0.0)`
Specify the renderer target in XML:
```kotlin theme={null}
```
Alternatively, when initializing Rive:
```kotlin theme={null}
Rive.init(applicationContext, defaultRenderer = RendererType.Rive)
```
The Rive Renderer is available in the Flutter runtime as of `0.14.0`. Use the latest version of the `rive` package for the newest fixes and improvements.
The Rive Renderer is exposed through `rive_native`, which is included as a dependency of the `rive` package. See [Rive Native](/docs/runtimes/flutter/rive-native) for more information.
When creating a Rive `File` or `FileLoader`, you need to specify a factory to use:
* `Factory.rive` for the Rive renderer
* `Factory.flutter` for the Flutter renderer (Skia or Impeller)
```dart theme={null}
// Rive renderer
File.asset("assets/vehicles.riv", riveFactory: Factory.rive)
// Flutter renderer
File.asset("assets/vehicles.riv", riveFactory: Factory.flutter)
```
You can use different renderers for different graphics in your app.
Some considerations when choosing a renderer:
* If you plan on showing many Rive graphics that are all drawing to different Rive widgets consider using a [RivePanel](/docs/runtimes/flutter/flutter#rivepanel) with `Factory.rive` to draw multiple graphics to the same texture to reduce the overhead of allocating native render targets and textures. Or make use of `Factory.flutter`.
* If you are showing a complex graphic, consider using `Factory.rive` to take advantage of the Rive renderer's optimizations.
* Vector Feathering is only available with `Factory.rive`, so if you need that feature, use the Rive renderer.
# Angular
Source: https://rive.app/docs/runtimes/community-runtimes/angular
A modern Angular wrapper runtime for Rive
This is not a Rive owned or maintained project.
See the [GitHub repository](https://github.com/Grandgular/rive) for additional information.
# C#
Source: https://rive.app/docs/runtimes/community-runtimes/c-sharp
Community maintained C# (UWP) runtime for Rive
This is not a Rive owned or maintained project.
See the [GitHub repository](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/RivePlayer/samples/RivePlayer.md) for additional information.
# Qt / QtQuick
Source: https://rive.app/docs/runtimes/community-runtimes/qt-quick
A Qt / QtQuick Renderer to draw Rive Graphics in QML.
This is not a Rive owned or maintained project.
See the [GitHub repository](https://github.com/basysKom/RiveQtQuickPlugin) for additional information.
# RiveCMP
Source: https://rive.app/docs/runtimes/community-runtimes/rive-cmp
A Compose Multiplatform wrapper library for integrating Rive animations
This is not a Rive owned or maintained project.
This library provides a unified API to use rive-android, rive-ios, and @rive-app/canvas seamlessly across Android, iOS, and Web platforms in Compose Multiplatform.
See the [GitHub repository](https://github.com/muazkadan/Rive-CMP) for additional information.
# Asset Loading
Source: https://rive.app/docs/runtimes/cpp/asset-loading
Resolve out-of-band images, fonts, and audio.
A `.riv` file can either embed asset bytes (images, fonts, audio, scripts) **in-band**
or reference them by CDN UUID and let the runtime fetch them. Implement
`FileAssetLoader` to control the second path — resolving from disk, the
network, or your own asset pipeline.
## The `FileAssetLoader` Interface
```cpp theme={null}
#include "rive/file_asset_loader.hpp"
#include "rive/assets/file_asset.hpp"
class FileAssetLoader : public RefCnt {
public:
virtual bool loadContents(FileAsset& asset,
Span inBandBytes,
Factory* factory) = 0;
};
```
`loadContents` is called once per asset during `File::import`. You return:
* `true` — you handled it. Either you populated `asset` synchronously, or
you kicked off async work and will populate it later.
* `false` — fall back to the in-band bytes (if any).
`asset` arrives typed: cast it to the concrete subclass to populate it.
| Asset type | Class | How to populate |
| ---------- | ------------ | ------------------------------------------------ |
| Image | `ImageAsset` | `asset.renderImage(factory->decodeImage(bytes))` |
| Font | `FontAsset` | `asset.font(factory->decodeFont(bytes))` |
| Audio | `AudioAsset` | `asset.audioSource(factory->decodeAudio(bytes))` |
## Sync Example: Load by File Name
```cpp theme={null}
#include "rive/file_asset_loader.hpp"
#include "rive/assets/image_asset.hpp"
#include "rive/assets/font_asset.hpp"
#include "rive/assets/audio_asset.hpp"
#include
#include
#include
#include
class DiskAssetLoader : public rive::FileAssetLoader {
public:
explicit DiskAssetLoader(std::filesystem::path root)
: m_root(std::move(root)) {}
bool loadContents(rive::FileAsset& asset,
rive::Span inBandBytes,
rive::Factory* factory) override
{
// Prefer in-band bytes when present.
if (inBandBytes.size() > 0) return false;
auto path = m_root / asset.uniqueFilename();
std::ifstream f(path, std::ios::binary);
if (!f) return false;
std::vector bytes((std::istreambuf_iterator(f)), {});
rive::Span span{bytes.data(), bytes.size()};
if (auto* img = dynamic_cast(&asset)) {
img->renderImage(factory->decodeImage(span));
return true;
}
if (auto* fnt = dynamic_cast(&asset)) {
fnt->font(factory->decodeFont(span));
return true;
}
if (auto* aud = dynamic_cast(&asset)) {
aud->audioSource(factory->decodeAudio(span));
return true;
}
return false;
}
private:
std::filesystem::path m_root;
};
```
`asset.uniqueFilename()` is the editor-assigned name with extension; use
`asset.cdnUuidStr()` if you index by CDN UUID instead.
## Wiring It Up
```cpp theme={null}
rcp loader = make_rcp("assets/");
ImportResult result;
rcp file = File::import(bytes, factory, &result, loader);
```
The loader is reference-counted — `File` keeps it alive for the file's
lifetime so async loads can complete after `import` returns.
## Async Loading
For async (HTTP, decoder thread, etc), return `true` from `loadContents`
**without** calling `renderImage` / `font` / `audioSource`, then populate the
asset later from any thread:
```cpp theme={null}
bool loadContents(FileAsset& asset, Span, Factory* factory) override {
auto* image = dynamic_cast(&asset);
if (!image) return false;
rcp keepAlive = ref_rcp(image);
fetchAsync(image->cdnUuidStr(), [keepAlive, factory](std::vector bytes) {
// Assumes this callback is dispatched to the render thread — see the
// warning below about decoder thread-safety.
rcp ri =
factory->decodeImage({bytes.data(), bytes.size()});
keepAlive->renderImage(std::move(ri));
// The next advanceAndApply will pick up the new image.
});
return true;
}
```
Decoders (`Factory::decodeImage`, `Factory::decodeFont`,
`Factory::decodeAudio`) are **not guaranteed thread-safe** for every
backend. If you decode off-thread, decode into a CPU-side representation
and finalize the GPU upload back on the render thread, or use a
thread-safe `Factory` if your backend supports it.
## Built-In Loaders
For simple cases the runtime ships a relative-path loader you can extend:
```cpp theme={null}
#include "rive/relative_local_asset_loader.hpp"
rcp loader =
make_rcp("/path/to/assets");
```
It loads files by `uniqueFilename()` from a directory on disk — handy for
samples and tooling.
## When In-Band Bytes Already Cover Everything
If your `.riv` file embeds all of its assets, you can skip the loader
entirely. `inBandBytes` will be non-empty for those assets and the runtime
will decode them with the `Factory` you provided.
# Command Queue
Source: https://rive.app/docs/runtimes/cpp/command-queue
Drive the C++ runtime from a separate thread via CommandQueue and CommandServer.
`CommandQueue` is an asynchronous, thread-safe alternative to the direct
`File` / `Artboard` / `StateMachineInstance` API documented elsewhere. Your
application thread enqueues commands (load a file, advance a state machine,
forward a pointer event) and a `CommandServer` worker drains them on the
render thread.
Use it when:
* Your app and render threads are separate, and you don't want to
cross-thread-call into the direct API.
* You want a Rive-content thread that's decoupled from the GPU thread.
* You need a stable handle-based identity for `File` / `Artboard` /
`StateMachineInstance` objects across thread boundaries.
If your render loop and your app logic are already on the same thread,
prefer the direct API — `CommandQueue` adds indirection and listener
plumbing you don't need.
## Architecture
* `CommandQueue` is **reference-counted** (`rcp`) and
thread-safe. The app thread keeps one; the server holds the other end.
* `CommandServer` owns the real `File`, `ArtboardInstance`, and
`StateMachineInstance` objects. They never escape the render thread.
* All cross-thread identifiers are typed **handles**:
`FileHandle`, `ArtboardHandle`, `StateMachineHandle`,
`ViewModelInstanceHandle`, `RenderImageHandle`, `FontHandle`,
`AudioSourceHandle`. The app thread holds handles; the server resolves
them to real objects.
* Async results (file loaded, state machine settled, image decoded) come
back via **listener callbacks** registered against handles.
## Setting Up the Queue and Server
```cpp theme={null}
#include "rive/command_queue.hpp"
#include "rive/command_server.hpp"
// Shared between threads.
rcp queue = make_rcp();
```
On the render thread, create a `CommandServer` and pump it:
```cpp theme={null}
// `factory` is your Factory* — usually your RenderContext.
CommandServer server(queue, factory);
// Drain pending commands once per frame...
server.processCommands();
// ...or block on a dedicated thread until disconnect.
// server.serveUntilDisconnect();
```
`processCommands()` is non-blocking — call it before each frame.
`serveUntilDisconnect()` is the run-loop variant for a worker thread:
blocks waiting for commands, and returns when the app thread calls
`queue->disconnect()`.
## Loading a File and Creating a State Machine
All these calls happen on the app thread. They enqueue commands and
return handles **immediately** — the actual work happens on the server
thread.
```cpp theme={null}
std::vector rivBytes = readFile("hero.riv");
FileHandle file = queue->loadFile(std::move(rivBytes));
ArtboardHandle artboard = queue->instantiateDefaultArtboard(file);
StateMachineHandle sm = queue->instantiateDefaultStateMachine(artboard);
```
The handles are valid to use immediately even though the load hasn't
happened yet — subsequent commands queued against them will execute in
order on the server.
## Advancing and Drawing
```cpp theme={null}
// App thread:
queue->advanceStateMachine(sm, /* dt = */ 1.f / 60.f);
```
Drawing is handled differently — you register a draw callback that runs
on the server thread when the queue tells it to. Pattern: create a
**draw key**, attach a callback, then tell the server to run that key.
See the `CommandQueue::drawCallback` and `runDraw` methods in
[`command_queue.hpp`](https://github.com/rive-app/rive-runtime/blob/main/include/rive/command_queue.hpp)
for the full draw-callback API; the canonical example is the
command-queue D3D11 sample in the rive monorepo
(`packages/sample_win32_d3d11_cq/`).
## Async Results: Listeners
Because commands run asynchronously, results come back via listener
callbacks. Each handle type has a matching listener with `on…` methods:
```cpp theme={null}
class MyFileListener : public CommandQueue::FileListener
{
public:
void onFileLoaded(const FileHandle, uint64_t requestId) override
{
// Safe to instantiate artboards now, etc.
}
};
MyFileListener listener;
listener.attach(queue, file); // register against this handle
```
Listeners available include:
* `FileListener` — `onFileLoaded`, `onFileDeleted`, `onArtboardsListed`,
`onViewModelsListed`, …
* `ArtboardListener` — `onArtboardInstanced`, `onArtboardError`,
`onArtboardDeleted`.
* `StateMachineListener` — `onStateMachineInstanced`,
`onStateMachineSettled` (called on the request that caused the state
machine to settle), …
* `ViewModelInstanceListener`, `RenderImageListener`, `FontListener`,
`AudioSourceListener` — for the other resource types.
`requestId` lets you correlate callbacks with specific commands.
Pass a non-zero `requestId` to the queue methods that accept one (e.g.
`deleteFile(handle, requestId)`) and your listener will receive the same
ID back.
## Pointer Events
Pointer events go through the queue too. `CommandQueue::PointerEvent`
carries the screen-space position; the server converts it to artboard
space using the state machine's most recent transform:
```cpp theme={null}
CommandQueue::PointerEvent ev;
ev.position = { mouseX, mouseY };
ev.kind = CommandQueue::PointerEvent::Kind::move;
queue->queuePointerEvent(sm, ev);
```
## Teardown
The app thread tells the server to stop:
```cpp theme={null}
queue->disconnect();
```
If the server is running `serveUntilDisconnect()`, that call returns
when the disconnect command arrives. After that, joining the server
thread is your responsibility.
Resource handles are cleaned up by enqueuing explicit delete commands
(or letting the server destructor reap them):
```cpp theme={null}
queue->deleteStateMachine(sm);
queue->deleteArtboard(artboard);
queue->deleteFile(file);
```
## When to Use the Direct API Instead
The direct `File` / `Artboard` / `StateMachineInstance` API documented in
the other pages on this site is simpler when:
* Your app already drives Rive from the render thread.
* You don't need handle-based identity across threads.
* You want synchronous return values rather than listener callbacks.
`CommandQueue` exists for the multithreaded case — it's the right tool
for engines, render servers, and apps with a dedicated GPU thread.
# Data Binding
Source: https://rive.app/docs/runtimes/cpp/data-binding
Drive Rive ViewModels from C++.
Rive's **View Models** expose strongly-typed properties
(numbers, strings, colors, booleans, enums, triggers, lists, nested view
models, image and artboard references) that an artboard binds to. From C++
you instance a view model, mutate properties, and the bound visuals update
on the next `advanceAndApply`.
## Concepts
* `ViewModelRuntime` — schema for a view model defined in the editor. Lives
in the `File`.
* `ViewModelInstanceRuntime` — typed wrapper around an instance of that
schema. Exposes the property API (`propertyNumber`, `propertyString`, etc).
You own it via `rcp<>`.
* `ViewModelInstance` — the underlying bindable instance. `Artboard` and
`StateMachineInstance` bind to this type. Get one from a
`ViewModelInstanceRuntime` with `.instance()`.
* `ViewModelInstance*Runtime` — typed handles for individual properties
(`…NumberRuntime`, `…StringRuntime`, etc).
## Creating an Instance
The simplest path is to ask the file for the artboard's default view model,
then create a default instance from it:
```cpp theme={null}
#include "rive/file.hpp"
#include "rive/viewmodel/runtime/viewmodel_runtime.hpp"
ViewModelRuntime* vm = file->defaultArtboardViewModel(artboard.get());
if (!vm) return; // artboard has no default view model
rcp instance = vm->createDefaultInstance();
if (instance) {
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());
}
```
For full control, look up a specific view model schema by index or name and
create instances from it:
```cpp theme={null}
ViewModelRuntime* vm = file->viewModelByName("Card");
if (!vm) return; // no view model with that name
size_t propCount = vm->propertyCount();
size_t instCount = vm->instanceCount();
rcp instance = vm->createDefaultInstance();
// alternatives — pick one and replace the line above:
// rcp instance = vm->createInstanceFromName("Hero");
// rcp instance = vm->createInstanceFromIndex(0);
// rcp instance = vm->createInstance(); // no editor preset; properties at type defaults
if (!instance) return;
artboard->bindViewModelInstance(instance->instance());
sm ->bindViewModelInstance(instance->instance());
```
Bind the same `ViewModelInstance` to **both** the artboard and the state machine.
The artboard binding drives layout-affecting properties; the state-machine binding
drives state-machine transitions and listener conditions.
## Reading & Writing Properties
All accessors are path-based — `/`-separated for nested view models.
```cpp theme={null}
auto* card = instance.get();
// Number
if (auto* score = card->propertyNumber("score")) {
score->value(42.0f);
float v = score->value();
}
// String
if (auto* title = card->propertyString("title")) {
title->value("Hello");
}
// Boolean
if (auto* on = card->propertyBoolean("isOpen")) {
on->value(true);
}
// Color (ARGB packed)
if (auto* col = card->propertyColor("accent")) {
col->value(0xFFE53935);
}
// Trigger (edge event)
if (auto* fire = card->propertyTrigger("fire")) {
fire->trigger();
}
// Enum (by string label)
if (auto* mood = card->propertyEnum("mood")) {
mood->value("happy");
}
```
### Nested View Models
```cpp theme={null}
// Option 1: deep path string.
auto* headerTitle = card->propertyString("header/title");
// Option 2: walk the tree.
rcp header = card->propertyViewModel("header");
auto* title = header->propertyString("title");
```
You can also **swap** a nested view model wholesale — useful for swapping a
list cell's data without rebuilding the artboard:
```cpp theme={null}
rcp newHeader = vm->createDefaultInstance();
card->replaceViewModel("header", newHeader.get());
```
### Lists
```cpp theme={null}
auto* items = card->propertyList("items");
// Append, insert, remove, replace, swap, count.
items->addInstance(rowInstance.get());
items->addInstanceAt(rowInstance.get(), 0);
items->removeInstanceAt(2);
items->swap(0, 1);
size_t n = items->size();
rcp row = items->instanceAt(0);
```
List items are themselves `ViewModelInstanceRuntime`s — same property API as
above.
### Image & Artboard Properties
```cpp theme={null}
auto* image = card->propertyImage("avatar");
image->value(decodedRenderImage.get()); // RenderImage*
auto* artboardRef = card->propertyArtboard("badge");
artboardRef->value(file->bindableArtboardNamed("Badge")); // rcp
```
## Lifecycle
* A `ViewModelInstanceRuntime` is a thin wrapper over a `ViewModelInstance`.
Hold the `rcp<>` for as long as anything binds to it.
* Property handles (`ViewModelInstanceNumberRuntime*`, etc) are owned by
the parent instance. Cache the pointer — it stays valid for the
instance's lifetime.
* After mutating properties, the next `sm->advanceAndApply(dt)` propagates
the changes through data binds and into rendering.
## When Properties Don't Exist
Every `propertyX(name)` getter returns `nullptr` if the name doesn't
resolve, so you can probe an instance safely:
```cpp theme={null}
if (auto* p = instance->propertyNumber("optional")) {
p->value(1.0f);
}
```
For introspection, walk the schema:
```cpp theme={null}
for (const PropertyData& p : instance->properties()) {
// p.name, p.type ∈ { number, string, boolean, color, enum, trigger,
// list, viewModel, image, artboard, ... }
}
```
# External Renderer
Source: https://rive.app/docs/runtimes/cpp/external-renderer
Plug Rive into your own GPU backend or 2D engine.
`RiveRenderer` and `rive::gpu::RenderContext` are one possible backend. The
core runtime is renderer-agnostic — anything you pass to
`StateMachineInstance::draw` only has to implement two interfaces:
* **`rive::Renderer`** — receives draw / clip / save / restore commands.
* **`rive::Factory`** — creates `RenderPath`, `RenderPaint`, `RenderImage`,
`RenderShader`, `RenderBuffer`, `Font`, `AudioSource` from raw bytes or
parameters during file import.
Implement both and Rive will route everything through them.
## Use Cases
* You already have a 2D vector engine (Skia, Direct2D, custom) and want
Rive to render through it.
* You're targeting a platform Rive doesn't ship a backend for.
* You want CPU-side hit-testing or analytics — render into a no-op
`Renderer` that records command counts.
If you only need Rive on Windows / macOS / iOS / Linux / Web / Android,
prefer the [built-in renderers](/docs/runtimes/cpp/renderers) — they're
faster and feature-complete.
## The `Renderer` Interface
```cpp theme={null}
class Renderer {
public:
virtual void save() = 0;
virtual void restore() = 0;
virtual void transform(const Mat2D&) = 0;
virtual void drawPath(RenderPath*, RenderPaint*) = 0;
virtual void clipPath(RenderPath*) = 0;
virtual void drawImage(const RenderImage*,
ImageSampler, BlendMode,
float opacity) = 0;
virtual void drawImageMesh(const RenderImage*, ImageSampler,
rcp verts_f32,
rcp uvs_f32,
rcp indices_u16,
uint32_t vertexCount,
uint32_t indexCount,
BlendMode, float opacity) = 0;
virtual void modulateOpacity(float opacity) = 0;
};
```
A few constraints worth knowing up front:
* `save` / `restore` form a stack and must capture: the current transform,
clip stack, and the modulated opacity.
* `clipPath` adds to the current clip — never replaces it.
* `modulateOpacity` is multiplicative; `0.5` then `0.2` ⇒ `0.1` effective
opacity until the next `restore`.
* `drawImageMesh` indices are 16-bit; vertex / UV buffers are
tightly-packed `float` pairs.
## The `Factory` Interface
```cpp theme={null}
class Factory {
public:
virtual rcp makeRenderBuffer(
RenderBufferType, RenderBufferFlags, size_t sizeInBytes) = 0;
virtual rcp makeLinearGradient(
float sx, float sy, float ex, float ey,
const ColorInt colors[], const float stops[], size_t count) = 0;
virtual rcp makeRadialGradient(
float cx, float cy, float radius,
const ColorInt colors[], const float stops[], size_t count) = 0;
virtual rcp makeRenderPath(RawPath&, FillRule) = 0;
virtual rcp makeEmptyRenderPath() = 0;
virtual rcp makeRenderPaint() = 0;
virtual rcp decodeImage(Span) = 0;
rcp decodeFont (Span); // non-virtual helper
rcp decodeAudio(Span); // non-virtual helper
};
```
Things to keep in mind:
* Gradient `colors[]` are packed ARGB ints; `stops[]` are normalized 0..1.
* `makeRenderPath(RawPath&, FillRule)` may **steal** the path's storage —
treat the input as moved-from after the call.
* `decodeImage` is called with raw PNG / JPEG / WebP bytes, etc. Decode in
whatever pixel format your renderer prefers and wrap the result in a
subclass of `RenderImage`.
* `decodeFont` / `decodeAudio` are non-virtual helpers that fan out to
Rive's built-in HarfBuzz / miniaudio paths. They are not subclass
override points; use the actual virtual `Factory` extension points for
custom font shaping or audio integration.
## RenderPath / RenderPaint Subclasses
Each holds the state your backend reads during drawing:
```cpp theme={null}
class MyPath : public RenderPath {
public:
void rewind() override { /* clear */ }
void moveTo(float x, float y) override { /* … */ }
void lineTo(float x, float y) override { /* … */ }
void cubicTo(float ox, float oy,
float ix, float iy,
float x, float y) override { /* … */ }
void close() override { /* … */ }
void addRenderPath(RenderPath*, const Mat2D&) override { /* … */ }
void addRawPath(const RawPath&) override { /* … */ }
};
class MyPaint : public RenderPaint {
public:
void style(RenderPaintStyle) override;
void color(unsigned int) override;
void thickness(float) override;
void join(StrokeJoin) override;
void cap(StrokeCap) override;
void blendMode(BlendMode) override;
void shader(rcp) override;
void invalidateStroke() override;
};
```
(Method signatures match `rive/command_path.hpp` (for `CommandPath`) and
`rive/renderer.hpp` (for `RenderPath` and `RenderPaint`) — read those for
the full set.)
## Wiring It Up
```cpp theme={null}
class MyFactory : public rive::Factory { /* ... */ };
MyFactory factory;
rcp file = File::import(bytes, &factory);
auto artboard = file->artboardDefault();
auto sm = artboard->defaultStateMachine();
MyRenderer renderer;
sm->advanceAndApply(dt);
sm->draw(&renderer);
```
There's no `RenderContext` in this path — your `Renderer` is responsible
for making the GPU calls (or buffering them, or counting them, or whatever
you want).
## Reference Implementations
All paths below are in the [rive-runtime](https://github.com/rive-app/rive-runtime) repo.
| Project | Uses | Where |
| ------------------------------ | -------------------------------- | ----------------- |
| `rive::gpu::RenderContext` | Pixel-local-storage GPU renderer | `renderer/src/` |
| `SkiaFactory` / `SkiaRenderer` | Skia | `skia/renderer/` |
| `CGFactory` / `CGRenderer` | CoreGraphics | `cg_renderer/` |
| `SokolFactory` | Sokol (tessellation) | `tess/src/sokol/` |
The Skia and CoreGraphics implementations are the most direct templates for
a "forward to an existing 2D engine" `Renderer`.
# File & Artboard
Source: https://rive.app/docs/runtimes/cpp/file-and-artboard
Importing .riv data and instancing artboards.
A Rive `.riv` file is a binary container of **artboards**, **animations**,
**state machines**, **view models**, and **assets**. The C++ API gives you
read-only access to that container (`File`, `Artboard`) and mutable instances
you can advance and draw (`ArtboardInstance`, `StateMachineInstance`).
## Importing a File
```cpp theme={null}
#include "rive/file.hpp"
ImportResult result;
rcp file = File::import(
Span{bytes.data(), bytes.size()},
factory, // a Factory* — usually your RenderContext
&result, // optional
assetLoader); // optional rcp
switch (result) {
case ImportResult::success: break;
case ImportResult::unsupportedVersion: /* runtime is older than file */ break;
case ImportResult::malformed: /* bad bytes */ break;
}
```
`File` is reference-counted (`rcp`). It owns the parsed object graph and
the assets imported in-band. Keep it alive for as long as any artboard
instance derived from it is alive.
The `Factory*` you pass in is responsible for constructing every
`RenderPath`, `RenderPaint`, `RenderImage`, font, and audio source the
file produces. When using Rive's GPU renderer, this is your
`RenderContext`. When using a custom renderer, it is your `Factory`
implementation.
## Determinism
```cpp theme={null}
File::deterministicMode = true;
```
A static flag that forces a fixed RNG seed and timestamp-driven scrolling for
all subsequent loads. Useful for golden-image tests and frame-by-frame
captures.
## Querying Artboards
```cpp theme={null}
size_t count = file->artboardCount();
std::string name = file->artboardNameAt(0);
Artboard* byName = file->artboard("Hero"); // by name
Artboard* byIndex = file->artboard(size_t{0}); // by index
Artboard* first = file->artboard(); // file's first artboard
```
`Artboard*` returned by these accessors is **read-only metadata** — do not
advance or draw it. To play an artboard, create an instance.
## Instancing
```cpp theme={null}
// Most common: copy of the file's default artboard.
std::unique_ptr ab = file->artboardDefault();
// Or by name / index:
auto namedAb = file->artboardNamed("Hero");
auto indexedAb = file->artboardAt(2);
```
Each call returns a **fresh, independent copy**. You can have many instances of
the same artboard playing at once — each with its own state machine, its own
data bindings, and its own layout.
```cpp theme={null}
size_t anims = ab->animationCount();
size_t states = ab->stateMachineCount();
std::string animName = ab->animationNameAt(0);
std::string smName = ab->stateMachineNameAt(0);
// Designer-marked default state machine, if any.
int defaultIdx = ab->defaultStateMachineIndex(); // -1 if none
```
## Creating a State Machine
`StateMachineInstance` is the unit of playback in C++ — what you advance
each frame, draw, and forward pointer events to.
```cpp theme={null}
#include "rive/animation/state_machine_instance.hpp"
std::unique_ptr sm = ab->defaultStateMachine();
if (!sm && ab->stateMachineCount() > 0) {
sm = ab->stateMachineAt(0);
}
```
## Sizing & Layout
Artboards have an intrinsic size set in the editor, plus an optional layout
mode (Yoga-driven). For non-layout fits the artboard stays at its intrinsic
size; for `Fit::layout` you drive width and height yourself:
```cpp theme={null}
#include "rive/layout.hpp"
if (fit == Fit::layout) {
ab->width(static_cast(windowWidth));
ab->height(static_cast(windowHeight));
} else {
ab->resetSize(); // back to intrinsic
}
// Re-evaluate layout before the next draw.
sm->advanceAndApply(0.f);
```
`Artboard::bounds()` returns the current 'rect' — feed it to `computeAlignment`
to map artboard-space into your viewport.
## File Lifetime
Reference-counted ownership keeps lifetimes simple:
```cpp theme={null}
rcp file = File::import(...);
auto ab = file->artboardDefault(); // unique_ptr
auto sm = ab->defaultStateMachine(); // unique_ptr
// Tear down in reverse order:
sm.reset();
ab.reset();
file = nullptr; // last rcp drops the File
```
Always destroy Rive objects **before** tearing down your `RenderContext` —
they hold references to GPU resources owned by the context.
# Getting Started
Source: https://rive.app/docs/runtimes/cpp/getting-started
Build rive-cpp and render a .riv file.
This guide walks you through cloning the runtime, compiling it, and getting a
`.riv` file on screen with a real GPU backend.
## Prerequisites
* A recent **clang** or **MSVC** that supports C++17.
* **git** — the build script will clone and bootstrap `premake5` itself.
* Platform SDK for your renderer of choice (Windows SDK for D3D, Xcode for
Metal, Vulkan SDK for Vulkan, etc).
Rive uses clang [vector
builtins](https://reviews.llvm.org/D111529). When building with clang, use the
latest version available — older toolchains may fail to compile the renderer.
## 1. Clone and Build the Runtime
```bash theme={null}
git clone https://github.com/rive-app/rive-runtime.git
cd rive-runtime/renderer
```
The runtime ships a build helper at `build/build_rive.sh` (with a
PowerShell wrapper `build_rive.ps1` for Windows). It installs the pinned
`premake5` version on first run and dispatches to the right build system
for your platform (gmake2 on macOS/Linux, MSBuild on Windows, etc).
```bash macOS / Linux theme={null}
../build/build_rive.sh release
```
```powershell Windows theme={null}
..\build\build_rive.ps1 release
```
Common variants:
* `build_rive.sh` (no args) — debug build for the host.
* `build_rive.sh release clean` — clean rebuild.
* `build_rive.sh ninja release` — use Ninja instead of make.
* `build_rive.sh ios release` / `build_rive.sh android release` —
cross-compile.
Build artifacts land in `out/release/` (or `out/debug/`). You'll link
against `librive.a` (or `rive.lib` on Windows), plus the per-backend
renderer libraries like `librive_pls_renderer.a`.
## 2. Add Headers to Your Project
The public include roots are:
```
rive-runtime/include # rive-cpp core
rive-runtime/renderer/include # GPU renderer (only if you use rive::gpu)
```
A minimal CMake snippet:
```cmake theme={null}
target_include_directories(my_app PRIVATE
${RIVE}/include
${RIVE}/renderer/include
)
target_link_libraries(my_app PRIVATE
rive
rive_pls_renderer
# plus your backend, e.g. d3d11, dxgi on Windows
)
```
## 3. Load a `.riv` File
```cpp theme={null}
#include "rive/file.hpp"
#include
#include
#include
using namespace rive;
std::vector readFile(const char* path) {
std::ifstream in(path, std::ios::binary);
return {std::istreambuf_iterator(in), {}};
}
// `factory` is a Factory* — usually your RenderContext (which inherits Factory).
auto bytes = readFile("hero.riv");
ImportResult result;
rcp file = File::import(bytes, factory, &result);
if (!file || result != ImportResult::success) {
// Bad file or unsupported version.
return;
}
```
## 4. Pick an Artboard and a State Machine
```cpp theme={null}
#include "rive/artboard.hpp"
#include "rive/animation/state_machine_instance.hpp"
std::unique_ptr artboard = file->artboardDefault();
std::unique_ptr sm = artboard->defaultStateMachine();
if (!sm && artboard->stateMachineCount() > 0) {
sm = artboard->stateMachineAt(0);
}
```
## 5. Advance and Draw
A render loop has three phases each frame: **advance**, **draw**, **flush**.
```cpp theme={null}
#include "rive/renderer/rive_renderer.hpp"
#include "rive/renderer/render_context.hpp"
void renderFrame(float dt) {
sm->advanceAndApply(dt);
RenderContext::FrameDescriptor frame{};
frame.renderTargetWidth = windowWidth;
frame.renderTargetHeight = windowHeight;
frame.clearColor = 0xff202020; // ARGB
renderContext->beginFrame(frame);
RiveRenderer renderer(renderContext.get());
renderer.save();
renderer.align(Fit::contain,
Alignment::center,
AABB(0, 0, windowWidth, windowHeight),
artboard->bounds());
sm->draw(&renderer);
renderer.restore();
RenderContext::FlushResources flush{};
flush.renderTarget = renderTarget.get();
renderContext->flush(flush);
}
```
Use a **fixed timestep** for `advanceAndApply` (e.g. 1/120s) and accumulate
real elapsed time. State machines are deterministic at fixed steps, which
makes playback reproducible across machines and frame rates. See
[Rendering Loop](/docs/runtimes/cpp/rendering-loop).
## 6. Forward Input
Route pointer events back through the state machine so
listeners and hit-testing work:
```cpp theme={null}
#include "rive/renderer.hpp"
Mat2D align = computeAlignment(Fit::contain,
Alignment::center,
AABB(0, 0, w, h),
artboard->bounds());
Vec2D toArtboard(int x, int y) {
return align.invertOrIdentity() * Vec2D{(float)x, (float)y};
}
sm->pointerMove(toArtboard(mouseX, mouseY));
sm->pointerDown(toArtboard(mouseX, mouseY));
sm->pointerUp(toArtboard(mouseX, mouseY));
```
## What's Next
Wire up your platform's GPU backend.
Advance state machines, forward pointer events, and react to state changes.
# C++ Runtime
Source: https://rive.app/docs/runtimes/cpp/overview
Load, advance, and render Rive content from C++.
The Rive C++ runtime (`rive-cpp`) is the lowest-level Rive runtime. It loads
`.riv` files, advances state machines and animations, and draws into any
`Renderer` — the
[Rive renderer](/docs/runtimes/cpp/renderers) (Metal, Vulkan, D3D11, D3D12,
OpenGL/WebGL) or [your own implementation](/docs/runtimes/cpp/external-renderer).
Higher level Rive runtimes (Apple, Android, Flutter, Unity, Unreal) wrap this
library.
Use the C++ runtime when you are:
* Embedding Rive in a C++ application or game engine.
* Targeting a platform that doesn't have a Rive runtime yet.
* Plugging Rive into your own renderer or render graph.
Build the runtime and put a `.riv` file on screen in under 100 lines of code.
Set up the GPU backend for your platform — D3D11, D3D12, Metal, Vulkan, or GL.
Import `.riv` files, query artboards, and create `ArtboardInstance`s.
Advance scenes, forward pointer events, and react to state changes.
Drive a Rive `ViewModel` from your application state.
Resolve out-of-band images, fonts, and audio with `FileAssetLoader`.
`beginFrame` / `flush` — what runs each frame and what it costs.
Implement `Renderer` and `Factory` to use your own GPU backend.
## Architecture at a Glance
**`File` / `Artboard` / `StateMachineInstance` know nothing about the GPU**,
and `Renderer` / `RenderContext` know nothing about Rive content. Either
half works on its own — render Rive content through your own `Renderer`
implementation, or drive `RiveRenderer` with non-Rive draw commands.
## Supported Platforms & APIs
| Backend | Headers | Class |
| -------------- | ----------------------------------------------------- | ------------------------- |
| D3D11 | `rive/renderer/d3d11/render_context_d3d_impl.hpp` | `RenderContextD3DImpl` |
| D3D12 | `rive/renderer/d3d12/render_context_d3d12_impl.hpp` | `RenderContextD3D12Impl` |
| Metal | `rive/renderer/metal/render_context_metal_impl.h` | `RenderContextMetalImpl` |
| Vulkan | `rive/renderer/vulkan/render_context_vulkan_impl.hpp` | `RenderContextVulkanImpl` |
| OpenGL / WebGL | `rive/renderer/gl/render_context_gl_impl.hpp` | `RenderContextGLImpl` |
The C++ runtime is also the foundation for Rive's [iOS/macOS](/docs/runtimes/apple),
[Android](/docs/runtimes/android), and [Flutter](/docs/runtimes/flutter) runtimes — those
platforms ship a thin wrapper, not a separate engine.
## Source & License
* GitHub: [rive-app/rive-runtime](https://github.com/rive-app/rive-runtime)
* License: MIT
* Build system: [premake5](https://premake.github.io/)
# Renderers
Source: https://rive.app/docs/runtimes/cpp/renderers
Set up the GPU backend for your platform.
`rive::gpu::RenderContext` is the API-agnostic frontend to Rive's GPU
renderer. You create one of the per-backend `*Impl::MakeContext` factories,
hand it to your render loop, and the rest of the C++ API stays identical.
`RenderContext` also implements `rive::Factory`, so the same object you pass to
`File::import` is the one that owns your GPU resources.
Header: `rive/renderer/d3d11/render_context_d3d_impl.hpp`
```cpp theme={null}
#include "rive/renderer/d3d11/render_context_d3d_impl.hpp"
using namespace rive::gpu;
ComPtr device;
ComPtr context;
// ... create device + context with D3D_FEATURE_LEVEL_11_1 ...
D3DContextOptions options;
options.isIntel = adapterDesc.VendorId == 0x163C ||
adapterDesc.VendorId == 0x8086 ||
adapterDesc.VendorId == 0x8087;
std::unique_ptr rc =
RenderContextD3DImpl::MakeContext(device, context, options);
auto* impl = rc->static_impl_cast();
rcp target = impl->makeRenderTarget(width, height);
// Each frame: bind the current backbuffer to the render target.
target->setTargetTexture(backbufferTexture);
```
Swap-chain notes:
* Set `BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_UNORDERED_ACCESS`.
* Use `DXGI_FORMAT_R8G8B8A8_UNORM` (or the `_SRGB` variant); other formats
may force the renderer onto an offscreen path.
* Set `options.isIntel = true` on Intel HD Graphics — works around a driver
bug in pixel-local-storage emulation.
For a working reference, see Rive's
[`tests/player`](https://github.com/rive-app/rive-runtime/tree/main/tests/player)
sample app and the D3D-specific wiring in
[`tests/common/offscreen_rendertarget_d3d.cpp`](https://github.com/rive-app/rive-runtime/blob/main/tests/common/offscreen_rendertarget_d3d.cpp).
Header: `rive/renderer/d3d12/render_context_d3d12_impl.hpp`
```cpp theme={null}
#include "rive/renderer/d3d12/render_context_d3d12_impl.hpp"
using namespace rive::gpu;
ComPtr device;
ComPtr initList; // recording state
D3DContextOptions options;
std::unique_ptr rc =
RenderContextD3D12Impl::MakeContext(device, initList.Get(), options);
auto* impl = rc->static_impl_cast();
rcp target = impl->makeRenderTarget(width, height);
```
Each frame, populate `RenderContext::FlushResources::externalCommandBuffer`
with the `ID3D12GraphicsCommandList` that should record Rive's draws, and
bump `currentFrameNumber` / `safeFrameNumber` so the renderer can recycle
buffers safely against your fence values.
The init command list passed to `MakeContext` must be in the **recording**
state. The renderer uses it to upload static buffers. Submit it (and wait
for the GPU to finish) before issuing any frames.
Header: `rive/renderer/metal/render_context_metal_impl.h`
```objc theme={null}
#import "rive/renderer/metal/render_context_metal_impl.h"
using namespace rive::gpu;
id device = MTLCreateSystemDefaultDevice();
RenderContextMetalImpl::ContextOptions opts;
std::unique_ptr rc =
RenderContextMetalImpl::MakeContext(device, opts);
```
Pass an `id` per frame via
`FlushResources::externalCommandBuffer`. Rive does **not** present — you
own the `CAMetalLayer` and the `present` call.
Header: `rive/renderer/vulkan/render_context_vulkan_impl.hpp`
```cpp theme={null}
#include "rive/renderer/vulkan/render_context_vulkan_impl.hpp"
using namespace rive::gpu;
VulkanFeatures features;
features.independentBlend = true; // detected from device features
// ... fill out from VkPhysicalDeviceFeatures ...
std::unique_ptr rc =
RenderContextVulkanImpl::MakeContext(
instance,
physicalDevice,
device,
features,
vkGetInstanceProcAddr);
```
Each frame:
* Set `FlushResources::externalCommandBuffer` to a recording
`VkCommandBuffer`.
* Set `FlushResources::currentFrameNumber` to the frame index you'll signal,
and `safeFrameNumber` to the most recent frame whose fence you've waited on.
Vulkan is the only backend that supports `FrameDescriptor::virtualTileWidth`
/ `virtualTileHeight` (frame splitting), useful for letting other
workloads pre-empt Rive on tiled GPUs.
Header: `rive/renderer/gl/render_context_gl_impl.hpp`
```cpp theme={null}
#include "rive/renderer/gl/render_context_gl_impl.hpp"
#include "rive/renderer/gl/render_target_gl.hpp"
using namespace rive::gpu;
RenderContextGLImpl::ContextOptions opts;
// opts.disableFragmentShaderInterlock = true; // if your driver lies
std::unique_ptr rc = RenderContextGLImpl::MakeContext(opts);
```
Construct a render target that wraps either an external texture or an
existing FBO — the doc graph stays the same, only the binding differs:
```cpp theme={null}
// Render into a texture you own.
rcp target =
make_rcp(width, height);
target->setTargetTexture(externalTextureID); // GLuint, you keep ownership
// Or, render into an existing FBO you own.
rcp target =
make_rcp(
width, height, fboId, sampleCount);
```
Prefer `TextureRenderTargetGL` when you can — `FramebufferRenderTargetGL`
often has to render to an offscreen texture and blit back, because the
external FBO is usually not readable.
Capability tiers Rive will auto-select between:
* **Pixel Local Storage** (best — Apple GPUs, modern mobile).
* **Fragment Shader Interlock** (NVIDIA, Intel via `GL_INTEL_…`).
* **R/W Texture / EXT\_shader\_pixel\_local\_storage** fallbacks.
* **MSAA** path on truly minimal drivers.
For WebGL 2: the same header builds against Emscripten — `MakeContext`
will pick the WebGL-compatible PLS implementation automatically.
## Picking Modes per Frame
`RenderContext::FrameDescriptor` carries flags that change the rendering
algorithm:
| Field | Effect |
| ----------------------- | ------------------------------------------------------------------- |
| `loadAction` | `clear` (default) / `preserveRenderTarget` / `dontCare`. |
| `clearColor` | ARGB; only used when `loadAction == clear`. |
| `msaaSampleCount` | Nonzero forces MSAA mode and disables PLS. |
| `disableRasterOrdering` | Forces the atomic path even where rasterizer ordering is supported. |
| `ditherMode` | `none` or `interleavedGradientNoise` (default). |
Most apps leave these at defaults. Override only when you see a specific issue
on a specific GPU.
## Choosing a Backend
| Platform | Recommendation |
| --------------- | -------------------------------------------------------------------------- |
| Windows desktop | **D3D11** if you only need 11-class hardware, **D3D12** for newer engines. |
| macOS / iOS | **Metal**. |
| Android | **Vulkan** on supported devices, **GLES** as fallback. |
| Linux desktop | **Vulkan**. |
| Web | **WebGL 2** via `RenderContextGLImpl` (build with Emscripten). |
| Game console | Talk to Rive — separate runtimes ship for PS5, Xbox, and Switch. |
# Rendering Loop
Source: https://rive.app/docs/runtimes/cpp/rendering-loop
What happens between beginFrame and flush — and how to drive it.
Each frame goes through three phases:
1. **Advance.** `sm->advanceAndApply(dt)` updates state machines, animations, layout, and data bindings.
2. **Record.** `RiveRenderer` records draw commands into the active `RenderContext`.
3. **Submit.** `renderContext->flush(...)` builds the GPU work and lets the
backend submit it.
```cpp theme={null}
// 1. Advance.
sm->advanceAndApply(dt);
// 2. Record draws.
RenderContext::FrameDescriptor frame{};
frame.renderTargetWidth = w;
frame.renderTargetHeight = h;
frame.clearColor = 0xff202020;
renderContext->beginFrame(frame);
RiveRenderer renderer(renderContext.get());
renderer.save();
renderer.align(Fit::contain, Alignment::center,
AABB(0, 0, w, h), artboard->bounds());
sm->draw(&renderer);
renderer.restore();
// 3. Submit.
RenderContext::FlushResources flush{};
flush.renderTarget = renderTarget.get();
renderContext->flush(flush);
```
## `FrameDescriptor`
Configures the upcoming frame. Values are reset every `beginFrame`.
| Field | Default | Purpose |
| ------------------------------------------ | -------------------------- | ---------------------------------------------------------- |
| `renderTargetWidth` / `renderTargetHeight` | 0 | Must match your render target. |
| `loadAction` | `clear` | `clear` / `preserveRenderTarget` / `dontCare`. |
| `clearColor` | 0 | ARGB; only used with `loadAction == clear`. |
| `msaaSampleCount` | 0 | Nonzero forces MSAA mode. |
| `disableRasterOrdering` | false | Forces atomic mode even when raster-ordering is supported. |
| `ditherMode` | `interleavedGradientNoise` | `none` to disable. |
| `virtualTileWidth` / `virtualTileHeight` | 0 | Vulkan-only frame tiling for pre-emption. |
## `FlushResources`
Carries everything the backend needs to submit the recorded work.
| Field | Use |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `renderTarget` | The `RenderTarget*` you bound this frame's backbuffer to. |
| `externalCommandBuffer` | Backend command buffer — `VkCommandBuffer` (Vulkan), `id` (Metal), `WGPUCommandEncoder` (WebGPU). Unused on D3D11 / GL. |
| `currentFrameNumber` | Monotonic frame ID for resource lifetime tracking. |
| `safeFrameNumber` | Most recent frame whose GPU work has retired (i.e. fence has signaled). Resources last used on or before this frame can be recycled. |
On D3D12, Vulkan, and Metal you must update `currentFrameNumber` and
`safeFrameNumber` every frame against your fence values. Otherwise the
renderer can't safely recycle staging buffers and you'll either see
overwrites mid-flight or unbounded memory growth.
## Fixed-Timestep Accumulator
State machines and animations are deterministic at fixed `dt`s. Wrap your
real-elapsed-time delta in an accumulator so playback is reproducible across
frame rates:
```cpp theme={null}
constexpr float kFixedSimDt = 1.f / 120.f;
constexpr float kMaxFrameDt = 0.25f; // cap after stalls
void tick(float realDt) {
if (realDt > kMaxFrameDt) realDt = kMaxFrameDt;
accumulator += realDt;
while (accumulator >= kFixedSimDt) {
sm->advanceAndApply(kFixedSimDt);
accumulator -= kFixedSimDt;
}
drawFrame();
}
```
The cap (`kMaxFrameDt`) prevents catch-up storms after the app gets paused
in a debugger or backgrounded by the OS.
## Resize Handling
Three things have to happen on a resize:
1. Resize your swap-chain / framebuffer.
2. Re-create the `RenderTarget`.
3. Either feed the new size into the artboard (for `Fit::layout`) or call
`resetSize()`, then run `advanceAndApply(0)` so layout solves before the
next draw.
```cpp theme={null}
void onResize(uint32_t w, uint32_t h) {
swapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0);
auto* impl = renderContext->static_impl_cast();
renderTarget = impl->makeRenderTarget(w, h);
if (fit == Fit::layout) {
artboard->width(static_cast(w));
artboard->height(static_cast(h));
} else {
artboard->resetSize();
}
sm->advanceAndApply(0.f);
}
```
## Aligning Artboard to Viewport
Use `computeAlignment` (or `Renderer::align`) to map artboard-space into the
viewport. Stash the matrix — you'll need its inverse to convert pointer
coordinates back into artboard-space.
```cpp theme={null}
#include "rive/renderer.hpp"
Mat2D align = computeAlignment(
Fit::contain, Alignment::center,
AABB(0, 0, w, h),
artboard->bounds());
renderer.save();
renderer.transform(align);
sm->draw(&renderer);
renderer.restore();
// Later, on input:
Vec2D pt = align.invertOrIdentity() * Vec2D{(float)mx, (float)my};
sm->pointerMove(pt);
```
## When to Skip a Frame
`advanceAndApply` returns `true` if the state machine has more work; `false` if
everything has settled. For animations that have looped to rest, you can
skip both the advance step and the draw call, dropping CPU/GPU usage to
zero between user inputs.
Pointer events and external view-model writes can wake the state machine back up —
re-draw at least once after each.
## Tear-Down
```cpp theme={null}
sm.reset();
artboard.reset();
file = nullptr;
renderTarget = nullptr;
renderContext.reset(); // last
```
Always destroy Rive objects **before** the `RenderContext` and the
underlying GPU device. Rive objects hold reference-counted GPU resources;
killing the device first leaves them with dangling handles and crashes on
release.
# State Machines
Source: https://rive.app/docs/runtimes/cpp/state-machines
Advance state machines and forward pointer events.
`StateMachineInstance` is the unit of playback in C++:
```cpp theme={null}
class StateMachineInstance {
public:
bool advanceAndApply(float elapsedSeconds);
void draw(Renderer*);
HitResult pointerDown(Vec2D, int pointerId = 0);
HitResult pointerMove(Vec2D, float timeStamp = 0, int pointerId = 0);
HitResult pointerUp(Vec2D, int pointerId = 0);
HitResult pointerExit(Vec2D, int pointerId = 0);
};
```
To drive a state machine — set values, fire triggers, react to changes —
use **Data Binding**. See [Data Binding](/docs/runtimes/cpp/data-binding).
## Advancing
`advanceAndApply(dt)` runs solvers (state machine, animations, layout, data
bindings) for `dt` seconds and applies the results to the artboard's
component graph. Call it before every draw:
```cpp theme={null}
sm->advanceAndApply(deltaSeconds);
sm->draw(&renderer);
```
A return value of `true` means the state machine is still animating and the next
frame should re-draw. `false` means everything has settled.
Use a **fixed timestep** accumulator. State machines and animations are
numerically deterministic at fixed steps, which keeps playback identical
across frame rates. See [Rendering Loop](/docs/runtimes/cpp/rendering-loop).
### Forcing a Layout Pass
Pass `dt = 0` to re-solve layout without advancing time. Useful right after
resizing the window or changing the artboard's `width()` / `height()`:
```cpp theme={null}
artboard->width(newWidth);
artboard->height(newHeight);
sm->advanceAndApply(0.f);
```
## Pointer Events
A state machine listens for pointer events on `Listener` components placed
in the editor. Map your window-space coordinates into **artboard-local**
space before forwarding:
```cpp theme={null}
#include "rive/renderer.hpp"
Mat2D align = computeAlignment(
Fit::contain,
Alignment::center,
AABB(0, 0, windowWidth, windowHeight),
artboard->bounds());
Vec2D toArtboard(int x, int y) {
return align.invertOrIdentity() * Vec2D{(float)x, (float)y};
}
sm->pointerMove(toArtboard(mx, my));
sm->pointerDown(toArtboard(mx, my));
sm->pointerUp(toArtboard(mx, my));
sm->pointerExit(toArtboard(mx, my));
```
`HitResult` returns one of three values, which tells you how to route the
same event to other UI behind Rive:
* `none` — the event passed through Rive without firing a listener.
Forward it to whatever UI is behind.
* `hit` — a listener fired, but the shape it's on is transparent. Rive
isn't blocking the event; forward it as well.
* `hitOpaque` — a listener fired and the shape is opaque. Rive consumed
the event; don't forward.
## Reading State Changes
After each `advanceAndApply` you can introspect what happened on a
`StateMachineInstance`. Call directly on your `StateMachineInstance`:
```cpp theme={null}
for (size_t i = 0, n = sm->stateChangedCount(); i < n; ++i) {
const LayerState* s = sm->stateChangedByIndex(i);
// s->name(), s->is(), etc.
}
```
This is the hook you use to react in C++ to states defined in the `.riv`
file — log analytics, play a sound, fire a callback into your engine.
# Rive Runtime Demos & Starters
Source: https://rive.app/docs/runtimes/demos
Quick examples to get you up and running.
# Flutter API Reference
Source: https://rive.app/docs/runtimes/flutter/api-reference
Explore the full Rive Flutter API, including widgets, controllers, and runtime classes.
If you're just getting started, check out the [Flutter Getting Started](/docs/runtimes/flutter/flutter) guide instead.
Open the full API docs on pub.dev
# Artboards
Source: https://rive.app/docs/runtimes/flutter/artboards
Selecting which artboard to render at runtime
For more information on creating artboards in the Rive editor, please refer to [Artboards](/docs/editor/fundamentals/artboards).
## Choosing an Artboard
When a Rive object is instantiated or when a Rive file is rendered, you can specify the artboard to use. If no artboard is given, the [default artboard](/docs/editor/fundamentals/artboards#default-state-machine), as set in the Rive editor, is used. If no default artboard is set, the first artboard is used.
Only one artboard can be rendered at a time.
### Creating an artboard
Manually create an artboard:
```dart theme={null}
// Default artboard
final artboard = riveFile.defaultArtboard();
// Artboard named
final artboard = riveFile.artboard('My Artboard');
// Artboard at index
final artboard = riveFile.artboardAt(0);
```
### Specifying an artboard
Specify the artboard to use in `RiveWidgetController` or `RiveWidgetBuilder`:
```dart theme={null}
// Default artboard
final artboardSelector = ArtboardSelector.byDefault();
// Artboard named
final artboardSelector = ArtboardSelector.byName('My Artboard');
// Artboard at index
final artboardSelector = ArtboardSelector.byIndex(0);
// Pass to RiveWidgetController
final controller = RiveWidgetController(
riveFile,
artboardSelector: artboardSelector,
);
// Pass to RiveWidgetBuilder
return RiveWidgetBuilder(
fileLoader: fileLoader,
artboardSelector: ArtboardSelector.byName('My Artboard'),
builder: (context, state) {
// return a widget
},
);
```
# Caching a Rive File
Source: https://rive.app/docs/runtimes/flutter/caching-a-rive-file
Under most circumstances a `.riv` file should load quickly and managing the `RiveFile` yourself is not necessary. But if you intend to use the same `.riv` file in multiple parts of your application, or even on the same screen, it might be advantageous to load the file once and keep it in memory.
## Example Usage
In Flutter, you are responsible for managing the lifecycle of a Rive file. You can create a `File` object directly, or use the `FileLoader` convenience class with `RiveWidgetBuilder`. In both cases, you must call `dispose()` on the object when it's no longer needed to free up memory.
```dart theme={null}
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class CachedPage extends StatefulWidget {
const CachedPage({super.key});
@override
State createState() => _CachedPageState();
}
class _CachedPageState extends State {
var _isRivLoaded = false;
// This is where our cached file will go.
late File _riveFile;
@override
void initState() {
super.initState();
// Once initialized, build the layout by updating _isRivLoaded.
_initRive().whenComplete(
() => setState(() {
_isRivLoaded = true;
}),
);
}
Future _initRive() async {
// Retrieve the Rive file from assets.
_riveFile = (await File.asset(
"assets/rewards_demo.riv",
riveFactory: Factory.rive,
))!;
}
@override
void dispose() {
_riveFile.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_isRivLoaded) {
// Both widgets can use the same Rive file because we cached it as state.
final widget1 = RiveWidget(controller: RiveWidgetController(_riveFile));
final widget2 = RiveWidget(controller: RiveWidgetController(_riveFile));
return Scaffold(
body: Row(
children: [
Expanded(child: widget1),
Expanded(child: widget2),
],
),
);
} else {
return CircularProgressIndicator();
}
}
}
```
To optimize memory usage, reuse the same `File` object across multiple `RiveWidget` instances if they use the same `.riv` file. This ensures the file is loaded only once and shared in memory.
After a `File` is disposed, it cannot be used again. To use the same `.riv` file, create a new `File` object.
#### Managing State
How you keep the Rive `File` alive and share it with widgets depends on your state management approach. For global access, load the file in `main` or during app startup, and expose it using a package like [Provider](https://pub.dev/packages/provider). If the file is only needed in a specific part of your app, consider loading the file only when required.
#### Memory
Managing the file yourself gives you fine-grained control over memory usage, especially when the same Rive file is used in multiple places or simultaneously in several widgets. Use [Flutter DevTools memory tooling](https://docs.flutter.dev/tools/devtools/memory#memory-view-guide) to monitor and optimize memory if needed.
#### Network Assets
To load a Rive file from the Internet, use `File.url('YOUR:URL')`. For network assets, cache the file in memory to avoid repeated downloads and unnecessary decoding of the file.
# Data Binding
Source: https://rive.app/docs/runtimes/flutter/data-binding
Connect your code to bound editor elements using View Models
Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the [Overview](/docs/editor/data-binding/overview).
# 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](#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.
If you're using a `RiveWidgetController`, you can skip the step of creating a `ViewModel`. Go to [View Model Instances](#view-model-instances).
```dart theme={null}
// Get reference to the File and Artboard
final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);
final artboard = file!.defaultArtboard()!;
// Get reference by name
file.viewModelByName("My View Model");
// Get reference by index
for (var i = 0; i < file.viewModelCount; i++) {
final indexedVM = file.viewModelByIndex(i);
}
// Get reference to the default view model for an artboard
final defaultVM = file.defaultArtboardViewModel(artboard);
// Dispose the view model when you're no longer using it
viewModel.dispose();
```
# 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:
| Type | Value |
| ----------------- | --------------- |
| Number | 0 |
| String | Empty string |
| Boolean | False |
| Color | 0xFF000000 |
| Trigger | Untriggered |
| Enum | The first value |
| Image | No image |
| Font | No font |
| Artboard | No artboard |
| List | Empty list |
| Nested view model | Null |
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.
In some samples, due to the wordiness of "view model instance", we use the abbreviation "VMI", as well as "VM" for "view model".
If you're using `RiveWidgetController`:
```dart theme={null}
// Get reference to the File
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);
// Create a controller
controller = RiveWidgetController(file!);
// Data bind by name
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
// Data bind by index
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
// Auto data bind
viewModelInstance = controller.dataBind(DataBind.auto());
// Bind some existing view model instance to the controller:
viewModelInstance = controller.dataBind(DataBind.byInstance(someViewModelInstance));
// Dispose of objects you created when no longer needed
viewModelInstance.dispose();
controller.dispose();
file.dispose();
```
If you want to manage the creation of view model instances yourself:
```dart theme={null}
final vm = file.viewModelByName("My View Model")!;
// Create blank
final vmiBlank = vm.createInstance();
// Create default
final vmiDefault = vm.createDefaultInstance();
// Create by index
for (int i = 0; i < vm.instanceCount; i++) {
final vmiIndexed = vm.createInstanceByIndex(i);
}
// Create by name
final vmiNamed = vm.createInstanceByName("My Instance");
// Dispose the view model instance
viewModelInstance.dispose();
```
### Binding
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.
If you're using `RiveWidgetController` the binding happens automatically when you call any of the following:
```dart theme={null}
viewModelInstance = controller.dataBind(DataBind.auto());
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
viewModelInstnace = controller.dataBind(DataBind.byInstance(someViewModelInstance));
```
Else, you need to make sure to bind the view model instance to the state machine, or artboard.
```dart theme={null}
final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);
final artboard = file!.defaultArtboard();
final stateMachine = artboard!.defaultStateMachine()!;
final vm = file.defaultArtboardViewModel(artboard)!;
final vmi = vm.createDefaultInstance()!;
// Bind to the state machine. This automatically binds to the artboard as well.
stateMachine.bindViewModelInstance(vmi);
// If you're not using a state machine, bind to the artboard
artboard.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.
```dart theme={null}
// Get reference to the File
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);
// Create a controller
controller = RiveWidgetController(file!);
// Auto data bind
viewModelInstance = controller.dataBind(DataBind.auto());
// Dispose of objects you created when no longer needed
viewModelInstance.dispose();
controller.dispose();
file.dispose();
```
# 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:
| Type | Supported |
| ---------------------- | --------- |
| Floating point numbers | ✅ |
| Booleans | ✅ |
| Triggers | ✅ |
| Strings | ✅ |
| Enumerations | ✅ |
| Colors | ✅ |
| Nested View Models | ✅ |
| Lists | ✅ |
| Images | ✅ |
| Artboards | ✅ |
For more information on version compatibility, see the [Feature Support](/docs/feature-support) page.
### Listing Properties
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.
```dart theme={null}
// Access on a ViewModel object
print("Properties: ${viewModel.properties}");
// Access on a ViewModelInstance object
print("Properties: ${viewModelInstance.properties}");
```
### Reading and Writing 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.
```dart theme={null}
// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;
final numberProperty = vmi.number("My Number Property")!;
// Get
final numberValue = numberProperty.value;
// Set
numberProperty.value = 10;
// Observe
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);
// Remove listener when done
numberProperty.removeListener(onNumberChange);
// Alternatively, clear all listeners
numberProperty.clearListeners();
// Dispose of the property to clear up resources when you're no longer using it
// This will call `clearListeners()` internally.
numberProperty.dispose();
```
### 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.
```dart theme={null}
// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;
final nestedNumberByChain = vmi
.viewModel("My Nested View Model")!
.viewModel("My Second Nested VM")!
.number("My Nested Number");
final 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.
```dart theme={null}
// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;
final numberProperty = vmi.number("My Number Property")!;
// Get
final numberValue = numberProperty.value;
// Set
numberProperty.value = 10;
// Observe
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);
// Remove listener when done
numberProperty.removeListener(onNumberChange);
// Alternatively, clear all listeners
numberProperty.clearListeners();
// Dispose of the property to clear up resources when you're no longer using it
// This will call `clearListeners()` internally.
numberProperty.dispose();
```
### Images
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.
See the [Flutter data binding images example](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding_images.dart).
```dart theme={null}
// Access the image property by path on a ViewModelInstance object
final imageProperty = viewModelInstance.image('my_image')!; // image property named "my_image"
// Create a RenderImage
final renderImage = await Factory.rive.decodeImage(bytes); // use `Factory.flutter` if you're using the Flutter renderer
// If the image is valid, update the image property value
if (renderImage != null) {
imageProperty.value = renderImage;
}
// You can also set the image property to null to clear it
imageProperty.value = null;
```
### Lists
List properties let you manage a dynamic set of view model instances at runtime. For example, you can build a to-do app where users can add and remove tasks in a scrollable Layout.
See the [Editor section](/docs/editor/data-binding/lists) on creating data bound lists.
A single list property can include different view model types, with each view model tied to its own Component, making it easy to populate a list with a variety of Component instances.
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](/docs/editor/data-binding/lists#view-model-list-property) editor documentation.
The list API in Flutter is designed to be similar to the [List](https://api.dart.dev/dart-core/List-class.html) class in Dart. It doesn't contain the full API spec of that class, but it does provide the most commonly used methods.
Working with lists can result in errors ([`RangeError`](https://api.flutter.dev/flutter/dart-core/RangeError-class.html)) being thrown if you try to access an index that is out of bounds, or perform other list operations that are not permitted. Similar to the Dart List API.
Access a list property by path on a `ViewModelInstance` object:
```dart Access a List property theme={null}
final todosProperty = viewModelInstance.list('todos')!; // list property named "todos"
print(todosProperty.length); // print the length of the list
```
To add an item you first need to create an instance of the view model that you want to add to the list:
```dart Create a blank view model instance theme={null}
final todoItemVM = riveFile.viewModelByName("TodoItem")!;
final todoItemInstance = todoItemVM.createInstance()!;
```
You can also create an instance from an existing instance (as exported in the Rive Editor), using:
* `createDefaultInstance()`
* `createInstanceByName('exercise')`
* `createInstanceByIndex(0)`.
Then add the instance to the list:
```dart Add an instance to the list theme={null}
todosProperty.add(todoItemInstance);
```
To remove a particular instance from the list, you can use the `remove` method:
```dart Remove an instance from the list theme={null}
todosProperty.remove(todoItemInstance);
```
Other operations:
```dart List operations theme={null}
// Remove at index
todosProperty.removeAt(0); // can throw
// Insert at index
todosProperty.insert(0, todoItemInstance); // can throw
// Swap
todosProperty.swap(0, 1); // can throw
// First
ViewModelInstance todo = todosProperty.first(); // can throw
// Last
ViewModelInstance todo = todosProperty.last(); // can throw
// First or null
ViewModelInstance? todo todosProperty.firstOrNull(); // will return null if the list is empty
// Last or null
ViewModelInstance? todosProperty.lastOrNull(); // will return null if the list is empty
// Access/set directly by index
final instance = todosProperty[0]; // can throw
todosProperty[0] = todoItemInstance; // can throw
// Instance at index
todosProperty.instanceAt(2); // can throw
// Length
todosProperty.length;
```
### Artboards
Artboard properties allows you to swap out entire components at runtime. This is useful for creating modular components that can be reused across different designs or applications, for example:
* Creating a skinning system that supports a large number of variations, such as a character creator where you can swap out different body parts, clothing, and accessories.
* Creating a complex scene that is a composition of various artboards loaded from various different Rive files (drawn to a single canvas/texture/widget).
* Reducing the size (complexity) of a single Rive file by breaking it up into smaller components that can be loaded on demand and swapped in and out as needed.
See the [Flutter data binding artboards example](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding_artboards.dart).
Artboard properties work with the `BindableArtboard` class, which is different from the regular `Artboard` class in the package.
`BindableArtboard` is a runtime wrapper for interacting with artboards through data binding. These instances reference existing artboards in your file, so no additional setup is required in the Rive Editor.
```dart theme={null}
// Artboard property to bind
final artboardProp = viewModelInstance.artboard('artboardPropertyName')!;
// Create a bindable artboard
final bindableArtboard = riveFile.artboardToBind('artboardName')!;
artboardProp.value = bindableArtboard;
```
### 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.
```dart theme={null}
// Access on a File object
print("Data enums: ${file.enums}");
```
# Examples
* [Data binding overview](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding.dart)
* [Data binding images](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding_images.dart)
* [Data binding artboards](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding_artboards.dart)
* [Data binding lists](https://github.com/rive-app/rive-flutter/blob/master/example/lib/examples/databinding_lists.dart)
# FAQ
Source: https://rive.app/docs/runtimes/flutter/faq
Common Flutter runtime issues and fixes.
## Where should I look first when my Flutter build fails?
Start with these docs:
* [Flutter runtime troubleshooting](/docs/runtimes/flutter/flutter#troubleshooting)
* [Rive Native troubleshooting](/docs/runtimes/flutter/rive-native#troubleshooting)
* [Building `rive_native`](/docs/runtimes/flutter/rive-native#building-rive-native)
* [Flutter migration guide](/docs/runtimes/flutter/migration-guide)
Most build issues come from setup/version mismatches, stale native artifacts, or skipped native setup. A clean rebuild usually helps:
```bash theme={null}
flutter clean
flutter pub get
flutter run
```
If native libraries were not downloaded, run the `rive_native` setup CLI for your target platform:
```bash theme={null}
dart run rive_native:setup --verbose --clean --platform
```
Replace `` with the platform you are building for (for example, `android` or `macos`).
You can also provide multiple platforms as a comma-separated list (for example, `android,ios,macos`).
If you still see setup errors, follow [Rive Native troubleshooting](/docs/runtimes/flutter/rive-native#troubleshooting). For Android-specific setup issues, see the [Android notes](/docs/runtimes/flutter/rive-native#android).
## `LateInitializationError: Field 'makeFlutterFactory' has not been initialized`
If you see an error like `LateInitializationError: Field 'makeFlutterFactory' has not been initialized`, make sure you initialize Rive before showing any Rive widgets.
Call `await RiveNative.init()` early (for example in `main()`), before creating or using `Factory.flutter`/`Factory.rive`.
```dart theme={null}
import 'package:flutter/widgets.dart';
import 'package:rive/rive.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await RiveNative.init();
runApp(const MyApp());
}
```
## "Too many active WebGL contexts" warnings on web
Every `Factory.rive` widget that owns its texture holds a live WebGL context, and browsers cap those. Exhausting the cap can also cause blank or frozen Rive widgets and CanvasKit context-loss errors. See [WebGL contexts (web)](/docs/runtimes/flutter/flutter#webgl-contexts-web) for how contexts are released and reused.
To stay under the cap:
* Draw multiple widgets into one shared texture with a [RivePanel](/docs/runtimes/flutter/flutter#rivepanel) — one context in total.
* Dispose Rive widgets you no longer show — that releases their WebGL contexts.
Set `RiveNative.debugRenderTextureLogging = true` to confirm you're hitting the cap: it logs texture create, reuse, and release with live WebGL context counts.
## How to enable 16KB page support on Android?
Rive Flutter `0.14.x` includes 16KB page support by default.
If you're on `0.13.x`, you need to specify the NDK version used by setting `rive.ndk.version=28.1.13356709` in your `gradle.properties` file.
This gives you 16KB page support and flexibility to control the NDK used for future updates.
For additional context, see [rive-flutter issue #479](https://github.com/rive-app/rive-flutter/issues/479).
# Flutter
Source: https://rive.app/docs/runtimes/flutter/flutter
Flutter runtime for Rive.
Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.
For more details, refer to the [feature support](/docs/feature-support/) and [choosing a renderer](/docs/runtimes/choose-a-renderer/) pages.
## Overview
This guide documents how to use the Rive Flutter runtime to easily integrate Rive graphics in your Flutter apps.
The latest version of Rive Flutter is currently published as a dev release
`0.14.0-dev.x`. This means that while the package is stable and ready for
production use, we are still actively developing new features and
improvements. We recommend using the latest dev version to take advantage of
the newest features and fixes.
Already using Rive Flutter? See our [Migration
Guide](/docs/runtimes/flutter/migration-guide) for information on adopting the
latest `0.14.x` version.
## Quick start
See our [example app](https://github.com/rive-app/rive-flutter/tree/master/example).
## Getting started
Follow the steps below to integrate Rive into your Flutter apps.
Check out Rive's [pub.dev](https://pub.dev/packages/rive) page to get the latest version.
```yaml theme={null}
# pubspec.yaml
dependencies:
rive: ^0.14.0-dev.6 # or latest dev version
```
Import the Rive runtime library in the file you're looking to integrate Rive animations into.
```dart theme={null}
import 'package:rive/rive.dart';
```
Consider doing a named import to avoid conflicts with other libraries:
```dart theme={null}
import 'package:rive/rive.dart' as rive;
```
You're encouraged to call `await RiveNative.init()` at the start of your app, or before you use Rive. For example, in `main.dart`. This will automatically be called the first time you load a Rive file, but if you want to ensure Rive is loaded before showing your first graphic, call it manually.
```dart theme={null}
import 'package:rive/rive.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
// Call init before using Rive.
await RiveNative.init();
runApp(const MyApp());
}
```
There are several ways to render Rive graphics in Flutter. We recommend using the `RiveWidget` and optionally the `RiveWidgetBuilder`, `RivePanel`, or a manually managed shared texture.
* `RiveWidget` is responsible for rendering the graphic and exposing common view configuration.
* `RiveWidgetBuilder` handles file loading, error states, and resource management automatically.
* `RivePanel` is a higher-level inherited widget that creates a shared texture to paint multiple `RiveWidget`s to. Only usable when using the Rive Renderer (`Factory.rive`). This can drastically improve performance when showing many Rive graphics at once by reducing the number of textures and avoiding WebGL context limitations on the web.
* `SharedRenderTexture.create()` and `RiveSurface` let you create and place a shared texture yourself, then pass it to `RiveWidget.sharedTexture`. Use this when the widgets drawing to the texture do not share a common `RivePanel` ancestor, such as siblings, overlays, or separate routes.
```dart theme={null}
class ExampleRiveBuilder extends StatefulWidget {
const ExampleRiveBuilder({super.key});
@override
State createState() => _ExampleRiveBuilderState();
}
class _ExampleRiveBuilderState extends State {
late final fileLoader = FileLoader.fromAsset("assets/vehicles.riv", riveFactory: Factory.rive);
@override
void dispose() {
fileLoader.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RiveWidgetBuilder(
fileLoader: fileLoader,
builder: (context, state) => switch (state) {
RiveLoading() => const Center(child: CircularProgressIndicator()),
RiveFailed() => ErrorWidget.withDetails(
message: state.error.toString(),
error: FlutterError(state.error.toString()),
),
RiveLoaded() => RiveWidget(
controller: state.controller,
fit: Fit.cover,
)
},
);
}
}
```
```dart theme={null}
class ExampleBasic extends StatefulWidget {
const ExampleBasic({super.key});
@override
State createState() => _ExampleBasicState();
}
class _ExampleBasicState extends State {
late File file;
late RiveWidgetController controller;
bool isInitialized = false;
@override
void initState() {
super.initState();
initRive();
}
void initRive() async {
file = (await File.asset("assets/vehicles.riv", riveFactory: Factory.rive))!;
controller = RiveWidgetController(file);
setState(() => isInitialized = true);
}
@override
void dispose() {
file.dispose();
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!isInitialized) {
return const Center(child: CircularProgressIndicator());
}
return RiveWidget(
controller: controller,
fit: Fit.cover,
);
}
}
```
Steps:
1. Wrap the `RiveWidget`s that should draw to the same texture with a single inherited `RivePanel`.
2. Set `useSharedTexture: true` in each `RiveWidget` that should draw to the shared texture.
3. (Optional) Set `drawOrder` in each `RiveWidget` to control stacking. Higher values draw on top. See [Draw Order](#draw-order).
```dart theme={null}
class ExampleRivePanel extends StatelessWidget {
const ExampleRivePanel({super.key});
@override
Widget build(BuildContext context) {
return const RivePanel(
backgroundColor: Colors.red,
child: ListViewExample(),
);
}
}
class ListViewExample extends StatefulWidget {
const ListViewExample({super.key});
@override
State createState() => _ListViewExampleState();
}
class _ListViewExampleState extends State {
late final fileLoader = FileLoader.fromAsset(
'assets/rating.riv',
riveFactory: Factory.rive,
);
@override
void dispose() {
fileLoader.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return MyRiveWidget(fileLoader: fileLoader);
},
);
}
}
class MyRiveWidget extends StatelessWidget {
const MyRiveWidget({super.key, required this.fileLoader});
final FileLoader fileLoader;
@override
Widget build(BuildContext context) {
return RiveWidgetBuilder(
fileLoader: fileLoader,
builder: (context, state) => switch (state) {
RiveLoading() => const Center(
child: Center(child: CircularProgressIndicator()),
),
RiveFailed() => ErrorWidget.withDetails(
message: state.error.toString(),
error: FlutterError(state.error.toString()),
),
RiveLoaded() => RiveWidget(
controller: state.controller,
fit: Fit.contain,
// Set this to true to draw to the nearest RivePanel
useSharedTexture: true,
)
},
);
}
}
```
Steps:
1. Create a `SharedRenderTexture` with `SharedRenderTexture.create()`.
2. Place its native surface in the widget tree with `RiveSurface`.
3. Pass the same texture to each `RiveWidget` with `sharedTexture`.
4. (Optional) Set `drawOrder` in each `RiveWidget` to control stacking. See [Draw Order](#draw-order).
5. Dispose the texture when you are done with it.
```dart theme={null}
class ExampleManualSharedTexture extends StatefulWidget {
const ExampleManualSharedTexture({super.key});
@override
State createState() =>
_ExampleManualSharedTextureState();
}
class _ExampleManualSharedTextureState
extends State {
late final SharedRenderTexture sharedTexture = SharedRenderTexture.create(
backgroundColor: Colors.transparent,
);
late final fileLoader = FileLoader.fromAsset(
'assets/rating.riv',
riveFactory: Factory.rive,
);
@override
void dispose() {
fileLoader.dispose();
sharedTexture.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Row(
children: List.generate(
3,
(index) => Expanded(
child: RiveWidgetBuilder(
fileLoader: fileLoader,
builder: (context, state) => switch (state) {
RiveLoading() => const Center(
child: CircularProgressIndicator(),
),
RiveFailed() => ErrorWidget.withDetails(
message: state.error.toString(),
error: FlutterError(state.error.toString()),
),
RiveLoaded() => RiveWidget(
controller: state.controller,
fit: Fit.contain,
sharedTexture: sharedTexture,
// Higher values draw on top
drawOrder: index,
),
},
),
),
),
),
Positioned.fill(
child: RiveSurface(sharedTexture: sharedTexture),
),
],
);
}
}
```
**From Asset Bundle:**
Make sure you add the Rive files to your asset bundle and reference them in `pubspec.yaml`:
```yaml theme={null}
# pubspec.yaml
assets:
- assets/vehicles.riv
```
```dart theme={null}
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromAsset("assets/vehicles.riv", riveFactory: Factory.rive);
// Using File directly
final file = await File.asset("assets/vehicles.riv", riveFactory: Factory.rive);
```
**From URL:**
```dart theme={null}
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromUrl("https://cdn.rive.app/animations/vehicles.riv", riveFactory: Factory.rive);
// Using File directly
final file = await File.url("https://cdn.rive.app/animations/vehicles.riv", riveFactory: Factory.rive);
```
**From Rive File:**
```dart theme={null}
// Using FileLoader (with RiveWidgetBuilder)
final fileLoader = FileLoader.fromFile(existingFile, riveFactory: Factory.rive);
```
## Key components
### `RiveWidget`
`RiveWidget` is responsible for displaying Rive graphics.
**Properties:**
* `controller` \[**required**]: The `RiveWidgetController` that manages the Rive graphic
* `fit`: How the artboard should fit within the widget (default: `contain`)
* `alignment`: How the artboard should be aligned within the widget (default: `center`)
* `hitTestBehavior`: How pointer events should be handled (default: `opaque`)
* `cursor`: The cursor to display when hovering over the widget (default: `defer`)
* `layoutScaleFactor`: Scale factor when using `Fit.layout` (default: `1.0`)
* `useSharedTexture`: Whether to use the nearest inherited shared texture from a [RivePanel](#rivepanel). Defaults to false. Ignored when `sharedTexture` is provided.
* `sharedTexture`: An explicit `SharedRenderTexture` to draw into, bypassing the ancestor-based `RivePanel` lookup. Use this with [`SharedRenderTexture` and `RiveSurface`](#sharedrendertexture-and-rivesurface) to share a texture across arbitrary parts of the widget tree.
* `drawOrder`: Stacking order when drawing to a shared texture with `Factory.rive` (default: `1`). Higher values draw on top. See [Draw Order](#draw-order).
* `renderResolution`: How the widget's backing texture is sized with `Factory.rive` (default: `RenderResolution.display()`). Not valid with `useSharedTexture`/`sharedTexture`. See [Render Resolution](#render-resolution).
### `RiveWidgetBuilder`
`RiveWidgetBuilder` is a higher-level widget that handles file loading, error states, and resource management automatically.
**Properties:**
* `fileLoader` \[**required**]: The `FileLoader` for loading the Rive file
* `builder` \[**required**]: Function that builds the widget based on state
* `artboardSelector`: Which artboard to use (default: `ArtboardDefault()`)
* `stateMachineSelector`: Which state machine to use (default: `StateMachineDefault()`)
* `dataBind`: How to bind view model data (optional)
* `controller`: Optional custom controller builder
* `onLoaded`: Callback when Rive state is loaded
* `onFailed`: Callback when Rive state fails to load
### `RivePanel`
`RivePanel` is a widget that creates a shared texture to paint multiple `RiveWidget`s to. This is useful when using `Factory.rive` and can significantly improve performance under certain conditions.
**When to use RivePanel:**
* When displaying multiple `RiveWidget`s in your app and they can be drawn to the same texture
* When you want to programmatically composite a scene that includes multiple Rive graphics (from multiple Rive files/artboards)
* When using `Factory.rive` (will report errors with `Factory.flutter`) and want to improve performance
* When you want to reduce the number of textures being drawn to
* When targeting web platforms to avoid [WebGL context limits](#webgl-contexts-web) through `Factory.rive`
**Performance considerations:**
* **Benefits**: Drawing multiple `RiveWidget`s to the same texture can drastically improve performance by reducing texture allocation overhead
* **Memory cost**: There is a memory cost in allocating a larger texture, though this may be offset by the reduced number of individual textures
* **Rendering limitations**: Drawing to the same surface means you cannot interleave Rive drawing commands with Flutter's drawing commands
* **Benchmarking recommended**: Performance characteristics vary by use case - what works for one scenario may not work for another
**Usage:**
```dart theme={null}
RivePanel(
backgroundColor: Colors.red, // Optional background color
child: YourWidgetWithMultipleRiveWidgets(),
)
```
**Important notes:**
* Only works with `Factory.rive` - has no effect with `Factory.flutter`
* Set `useSharedTexture: true` in your `RiveWidget`s to enable shared texture rendering
* Set `drawOrder` in your `RiveWidget`s to control stacking (see [Draw Order](#draw-order))
* Set `renderResolution` on the panel to control how its shared texture is sized (see [Render Resolution](#render-resolution))
* If you need to interleave Rive content with Flutter content, consider using separate `RivePanel`s or `Factory.flutter`
* For complex scenarios, benchmark both approaches to determine the best performance strategy
### `SharedRenderTexture` and `RiveSurface`
`SharedRenderTexture.create()` creates a user-owned shared texture that can be passed directly to `RiveWidget.sharedTexture`. Unlike `RivePanel`, this does not rely on inherited widget lookup, so it works when the `RiveWidget`s are siblings, live in separate subtrees, or are mounted through an `OverlayEntry`, dialog, bottom sheet, or another route.
Use `RiveSurface` to place the native texture in the widget tree. The `RiveWidget`s that receive the same `SharedRenderTexture` draw into that surface, even if they are not descendants of the surface.
```dart theme={null}
late final SharedRenderTexture sharedTexture = SharedRenderTexture.create(
backgroundColor: Colors.transparent,
);
@override
void dispose() {
sharedTexture.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
RiveWidget(
controller: controller,
sharedTexture: sharedTexture,
),
Positioned.fill(
child: RiveSurface(sharedTexture: sharedTexture),
),
],
);
}
```
**Important notes:**
* This API is experimental and only works with `Factory.rive`.
* `RiveWidget.sharedTexture` takes precedence over `useSharedTexture` when both are set.
* You must call `dispose()` on textures created with `SharedRenderTexture.create()` to release the native texture.
* `RiveSurface` is wrapped in `IgnorePointer` by default so pointer events are handled by the individual `RiveWidget`s. Set `ignorePointer: false` if pointer events should reach the native texture widget directly.
* Set `renderResolution` on the `RiveSurface` to control how the shared texture is sized (see [Render Resolution](#render-resolution)).
### Draw Order
When multiple `RiveWidget`s draw to the same shared texture - through a [RivePanel](#rivepanel) or an explicit [`sharedTexture`](#sharedrendertexture-and-rivesurface) - `RiveWidget.drawOrder` (default: `1`) controls how they stack:
* Higher values draw on top.
* Widgets with the same value stack in widget-tree order.
* Changing `drawOrder` on a mounted widget restacks it on the next frame.
`drawOrder` only applies when drawing to a shared texture with `Factory.rive`. When a widget owns its own texture, Flutter's normal paint order applies.
### Render Resolution
`Factory.rive` draws content into a backing texture. `RiveWidget.renderResolution` (default: `RenderResolution.display()`) controls how that texture is sized - the same way on every platform, including web. It changes backing resolution only; layout, fit, alignment, and hit testing are unaffected.
* `RenderResolution.display()` (default): layout size \* device pixel ratio \* any scale ancestors apply at paint time (for example a `FittedBox` or `Transform.scale`). Content stays sharp at its final on-screen size, and allocates for it.
* `RenderResolution.layout(scale: 1.0)`: layout size \* device pixel ratio \* `scale`. Ancestor transforms apply at composite time, so scaling the widget up stretches the texture (softer output) instead of reallocating it - render small and let a `FittedBox` upscale cheaply.
* `RenderResolution.fixed(width, height)`: an explicit size in physical pixels, independent of layout, device pixel ratio, and transforms. Never reallocated on resize.
For shared textures the surface owns the allocation: set `renderResolution` on the [RivePanel](#rivepanel) or [RiveSurface](#sharedrendertexture-and-rivesurface). Setting it on a `RiveWidget` alongside `useSharedTexture` or `sharedTexture` throws an assertion error.
`renderResolution` only applies with `Factory.rive`. `Factory.flutter` paints directly into Flutter's canvas at composite resolution.
### `RiveWidgetController`
`RiveWidgetController` manages the graphic.
**Creating a Controller:**
```dart theme={null}
// Using default artboard and state machine
final controller = RiveWidgetController(file);
// Specifying artboard and state machine
final controller = RiveWidgetController(
file,
artboardSelector: ArtboardSelector.byName("MyArtboard"),
stateMachineSelector: StateMachineSelector.byName("MyStateMachine"),
);
```
**Data Binding:**
```dart theme={null}
// Auto-bind with default view model instance
final viewModelInstance = controller.dataBind(DataBind.auto());
// Bind by specific instance
final viewModelInstance = controller.dataBind(DataBind.byInstance(myInstance));
// Bind by name
final viewModelInstance = controller.dataBind(DataBind.byName("MyViewModel"));
```
### File loading
The `FileLoader` class provides a unified way to load Rive files from different sources.
**Loading from Assets:**
```dart theme={null}
final fileLoader = FileLoader.fromAsset(
"assets/vehicles.riv",
riveFactory: Factory.rive,
);
```
**Loading from URL:**
```dart theme={null}
final fileLoader = FileLoader.fromUrl(
"https://example.com/animation.riv",
riveFactory: Factory.rive,
);
```
**Loading from Existing File:**
```dart theme={null}
final fileLoader = FileLoader.fromFile(
existingFile,
riveFactory: Factory.rive,
);
```
Or you can load files directly using the `File` class:
```dart theme={null}
// Load from asset
final file = await File.asset("assets/vehicles.riv", riveFactory: Factory.rive);
// Load from URL
final file = await File.url("https://example.com/animation.riv", riveFactory:
Factory.rive);
// Load from path
final file = await File.path("/path/to/animation.riv", riveFactory: Factory.rive);
// Load from bytes
final file = await File.decode(bytes, riveFactory: Factory.rive);
```
## Error handling
The Rive Flutter package provides specific exception types for different error scenarios:
* `RiveFileLoaderException`: Thrown when file loading fails
* `RiveArtboardException`: Thrown when artboard selection fails
* `RiveStateMachineException`: Thrown when state machine selection fails
* `RiveDataBindException`: Thrown when data binding fails
## Resource management
### Manual resource management (`RiveWidget`)
When using `RiveWidget` directly, you are responsible for managing all resources:
```dart theme={null}
@override
void dispose() {
// Dispose resources in reverse order of creation
viewModelInstance.dispose();
controller.dispose();
file.dispose();
super.dispose();
}
```
### Automatic resource management (`RiveWidgetBuilder`)
When using `RiveWidgetBuilder`, the widget automatically manages most resources. You only need to dispose the file loader:
```dart theme={null}
@override
void dispose() {
fileLoader.dispose();
super.dispose();
}
```
Because the resources are managed by the `RiveWidgetBuilder`, you will not be able to access the `RiveWidgetController` (and other state) after the widget is disposed. If you need to access the controller after the widget is disposed, consider creating the file and controller yourself.
The exception to this is the `FileLoader`, which you control. This loader can be reused across multiple `RiveWidgetBuilder` instances. The underlying `File` will only be loaded once. The `File` will be disposed when the `FileLoader` is disposed.
### WebGL contexts (web)
On the web, every `Factory.rive` widget that owns its texture renders into its own WebGL context, and browsers cap live contexts (around 16, shared with Flutter's own CanvasKit surfaces).
Disposing a Rive widget releases its context. A small reuse pool keeps a few canvases alive, so churn like route transitions stays cheap. Contexts the browser has evicted are detected and destroyed, never reused.
Set `RiveNative.debugRenderTextureLogging = true` to log texture create, reuse, and release with live WebGL context counts.
When showing many Rive widgets at once on web, draw them into a single shared texture with a [RivePanel](#rivepanel) - one WebGL context in total instead of one per widget.
## Specifying a renderer
When creating a Rive `File` or `FileLoader`, you need to specify a factory to use:
* `Factory.rive` for the Rive renderer
* `Factory.flutter` for the Flutter renderer (Skia or Impeller)
You can use different renderers for different graphics in your app.
Some considerations when choosing a renderer:
* If you plan on showing many Rive graphics that are all drawing to different Rive widgets consider using a [RivePanel](#rivepanel) with `Factory.rive` to draw multiple graphics to the same texture to reduce the overhead of allocating native render targets and textures. Or make use of `Factory.flutter`.
* If you are showing a complex graphic, consider using `Factory.rive` to take advantage of the Rive renderer's optimizations.
* Vector Feathering is only available with `Factory.rive`, so if you need that feature, use the Rive renderer.
For more information see [Choosing a Renderer](/docs/runtimes/choose-a-renderer/).
### Note on Flutter Rendering
[Impeller](https://docs.flutter.dev/perf/impeller) is replacing [Skia](https://skia.org/) to become the default renderer for all platforms. As such, there is a possibility of rendering and [performance](https://github.com/flutter/flutter/issues/134432) discrepancies when using the Rive Flutter runtime with platforms that use the Impeller renderer that may not have surfaced before. If you encounter any visual or performance errors at runtime compared to expected behavior in the Rive editor, we recommend trying the following steps to triage:
1. Try running the Flutter app with the `--no-enable-impeller` flag to use the Skia renderer. If the visual discrepancy does not show when using Skia, it may be a rendering bug on Impeller. However, before raising a bug with the Flutter team, try the second point below👇
```bash theme={null}
flutter run --no-enable-impeller
```
2. Try running the Flutter app on the latest `master` channel. It is possible that visual bugs may be resolved on the latest Flutter commits, but not yet released in the `beta` or `stable` channel.
3. If you are still seeing visual discrepancies with just the Impeller renderer on the latest master branch, we recommend raising a detailed issue to the [Flutter](https://github.com/flutter/flutter) Github repo with a reproducible example, and other relevant details that can help the team debug any possible issues that may be present.
## Troubleshooting
If you encounter issues with Rive in Flutter, consider the following:
* Ensure you have called `await RiveNative.init()` before using any Rive features.
* Check the console for any error messages related to Rive.
* Make sure your Rive files are correctly referenced in `pubspec.yaml` and exist in the specified paths.
* If using `RiveWidgetBuilder`, ensure you handle all possible states (loading, loaded, failed) in the builder function.
### Build errors
If you encounter build errors related to Rive, ensure that:
* You have the correct version of the Rive package in your `pubspec.yaml`.
* You have run `flutter pub get` to fetch the latest dependencies.
* You have reviewed the [Flutter FAQ](/docs/runtimes/flutter/faq) for common issues and questions.
If you're still having issues, please see the [Troubleshooting section](/docs/runtimes/flutter/rive-native#troubleshooting) in the Rive Native documentation.
## Manually building Rive native libraries
Rive automatically downloads the native libraries for you as part of the `rive_native` plugin.
However, if you need to manually build the native libraries, see the [build section](/docs/runtimes/flutter/rive-native#building-rive-native) in the Rive Native documentation.
## Resources
Rive Flutter:
* [GitHub](https://github.com/rive-app/rive-flutter)
* [pub.dev](https://pub.dev/packages/rive)
* [Example app](https://github.com/rive-app/rive-flutter/tree/master/example/)
Rive Native:
* [Rive Native overview](/docs/runtimes/flutter/rive-native)
* [pub.dev](https://pub.dev/packages/rive_native)
# Fonts
Source: https://rive.app/docs/runtimes/flutter/fonts
Loading and replacing fonts dynamically at runtime.
## Swapping Font Assets at Runtime
Fonts can be loaded dynamically at runtime. This allows you to localize your Rive content without increasing the file size of the exported .riv file.
Swapping a font asset replaces all instances of the font.
For more information, see [Loading Assets](/docs/runtimes/flutter/loading-assets).
## Fallback Fonts
Fallback fonts are **not yet supported in Flutter**.
Fallback fonts are **not supported on the web**.
For security reasons, browsers do not allow the canvas to access local system files, including fonts. As a result, only fonts explicitly provided to Rive can be used.
# Layout
Source: https://rive.app/docs/runtimes/flutter/layouts
Control how graphics are laid out within the canvas.
## The Fit Mode
A Rive graphic authored in the editor will not necessarily match the size of the container it is rendered into at runtime. We need to determine the behavior for this scenario, as no one size fits all.
The solution is choosing the fit mode. This is specified on the container and controls how Rive is scaled.
* `Layout`: Use the Rive layout engine to apply responsive layout to the artboard, matching the container dimensions. For this to work, the artboard must be designed with layouts in mind. See [Responsive Layouts](#responsive-layouts) for more information on how to use this option.
* `Contain`: **(Default)** Preserve aspect ratio and scale the artboard so that its larger dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will leave space on the shorter dimension's axis.
* `ScaleDown`: Preserve aspect ratio and behave like `Contain` when the artboard is larger than the container. Otherwise, use the artboard's original dimensions.
* `Cover`: Preserve aspect ratio and scale the artboard so that its smaller dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will clip the artboard on the larger dimension's axis.
* `FitWidth`: Preserve aspect ratio and scale the artboard width to match the container's width.
If the aspect ratios between the artboard and container do not match, this will result in either vertical clipping or space in the vertical axis.
* `FitHeight`: Preserve aspect ratio and scale the artboard height to match the container's height.
If the aspect ratios between the artboard and container do not match, this will result in either horizontal clipping or space in the horizontal axis.
* `Fill`: Do not preserve aspect ratio and stretch to the container's dimensions.
* `None`: Do not scale. Use the artboard's original dimensions.
For either dimension, if the artboard's dimension is larger, it will be clipped. If it is smaller, it will leave space.
### Alignment
In all options other than `Layout` and `Fill`, there is the possibility that the Rive graphic is clipped or leaves space within its container. Alignment determines how content aligns within the container. The following options are available.
* `TopLeft`
* `TopCenter`
* `TopRight`
* `CenterLeft`
* `Center` **(Default)**
* `CenterRight`
* `BottomLeft`
* `BottomCenter`
* `BottomRight`
### Bounds
This runtime exposes the bounding dimensions for the area in which the Rive content will render by providing the minimum and maximum x and y coordinates. These coordinates are relative to the container and all must be provided. These will override alignment settings.
* `minX`
* `minY`
* `maxX`
* `maxY`
### Applying the Fit Mode
Pass the `Fit` and `Alignment` to the `RiveWidget` widget.
```dart theme={null}
return RiveWidget(
controller: controller,
fit: Fit.contain,
alignment: Alignment.center,
);
```
Alternatively, you can also the set `fit` and `alignment` properties directly on any `RivePainter`, such as the `RiveWidgetController`:
```dart theme={null}
final controller = RiveWidgetController(riveFile);
controller.fit = Fit.contain;
controller.alignment = Alignment.center;
```
## Responsive Layouts
Rive’s layout feature lets you design resizable artboards with built-in responsive behavior, configured from the editor. Ensure the fit mode is set to **Layout** at runtime and the artboard will resize to fill its container according to the constraints defined in the editor.
Optionally you may provide a **layout scale factor** to multiply the scale of the content. This allows fine tuning the visual size within your container. This property only applies when setting the **Fit** mode to **Layout**.
For more Editor information and how to configure your graphic, see [Layouts Overview](/docs/editor/layouts/layouts-overview).
Pass the `Fit.layout` to the `RiveWidget` widget. This will automatically scale and resize the artboard to match the widget size.
You can also set the `layoutScaleFactor` to control the scale of the artboard. This is useful for adjusting the size of the artboard when using `Fit.layout`.
```dart theme={null}
return RiveWidget(
controller: controller,
fit: Fit.layout,
layoutScaleFactor: 2.0, // Optional: 2x scale of the layout,
);
```
Alternatively, you can also set the `fit` and `layoutScaleFactor` properties directly on any `RivePainter`, such as the `RiveWidgetController`:
```dart theme={null}
final controller = RiveWidgetController(riveFile);
controller.fit = Fit.layout;
controller.layoutScaleFactor = 2.0; // Optional: 2x scale of the layout
```
# Loading Assets
Source: https://rive.app/docs/runtimes/flutter/loading-assets
Loading and replacing assets dynamically at runtime
If you want to dynamically replace images, use image data binding.
Some Rive files may contain assets that can be embedded within the actual file binary, such as font, image, or audio files. The Rive runtimes may then load these assets when the Rive file is loaded. While this makes for easy usage of the Rive files/runtimes, there may be opportunities to load these assets in or even replace them at runtime instead of embedding them in the file binary.
There are several benefits to this approach:
* Keep the `.riv` files tiny without potential bloat of larger assets
* Dynamically load an asset for any reason, such as loading an image with a smaller resolution if the `.riv` is running on a mobile device vs. an image of a larger resolution for desktop devices
* Preload assets to have available immediately when displaying your `.riv`
* Use assets already bundled with your application, such as font files
* Sharing the same asset between multiple `.riv`s
## Methods for Loading Assets
There are currently three different ways to load assets for your Rive files.
In the Rive editor select the desired asset from the **Assets** tab, and in the inspector choose the desired export option:
### Embedded Assets
In the Rive editor, static assets can be included in the `.riv` file, by choosing the *"Embedded"* export type. As stated in the beginning of this page, when the Rive file gets loaded, the runtime will implicitly attempt to load in the assets embedded in the `.riv` as well, and you don't need to concern yourself with loading any assets manually.
**Caveat:** Embedded assets may bulk up the file size, especially when it comes to fonts when using Rive Text ([Text Overview](/docs/editor/text/text-overview)).
**Embedded is the default option.**
### Loading via Rive's CDN
In the Rive editor, you can mark an imported asset as a *"Hosted"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, but will be hosted on Rive's CDN. This means that at runtime when loading in the file, the runtime will see the asset is marked as "Hosted" and load the asset in from the Rive CDN, so that you don't need to concern yourself with loading anything yourself, and the file can still remain tiny.
**Caveat:** The app will make an extra call to a Rive CDN to retrieve your asset
Hosted assets are available on Voyager and Enterprise plans. [Learn more about
our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
### Image CDNs
Some image CDNs allow for on-the-fly image transformations, including resizing, cropping, and automatic format conversion based on the browser's and device's capabilities. These CDNs can host your Rive image assets. Note that for these CDNs, you may need to specify the accepted formats, for example, as part of the HTTP header request:
```html theme={null}
... headers: { Accept: 'image/png,image/webp,image/jpeg,*/*', } ...
```
Please see your CDN provider's documentation for additional information.
Rive supports the following image formats: **jpeg**, **png**, and **webp**
### Referenced Assets
In the Rive editor, you can mark an imported asset as a *"Referenced"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, and the responsibility of loading the asset will be handled by your application at runtime.
This option enables you to dynamically load in assets via a handler API when the runtime begins loading in the `.riv` file. This option is preferable if you have a need to dynamically load in a specific asset based on any kind of app/game logic, and especially if you want to keep the .riv file size small.
All referenced assets, including the `.riv`, will be bundled as a zip file when you export your animation.
**Caveat:** You will need to provide an asset handler API when loading in Rive which should do the work of loading in an asset yourself. See [Handling Assets](#handling-assets).
SVG assets can't currently be loaded at runtime as referenced assets.
This is because SVGs are converted to Rive vector objects, which are always embedded into the .riv.
## Handling Assets
### Examples
* See the [Rive Flutter example app](https://github.com/rive-app/rive-flutter/tree/master/example).
### Using the Asset Handler API
When instantiating a `File`, add an `assetLoader` callback to the list of parameters. This callback will be called for every asset the runtime detects from the `.riv` file on load, and will be responsible for either handling the load of an asset at runtime or passing on the responsibility and giving the runtime a chance to load it otherwise.
```dart Font Asset Example theme={null}
final fontFile = await File.asset(
'assets/acqua_text_out_of_band.riv',
riveFactory: Factory.rive,
assetLoader: (asset, bytes) {
// Replace font assets that are not embedded in the rive file
if (asset is FontAsset && bytes == null) {
final urls = [
'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf',
'https://cdn.rive.app/runtime/flutter/comic-neue.ttf',
'https://cdn.rive.app/runtime/flutter/inter.ttf',
'https://cdn.rive.app/runtime/flutter/inter-tight.ttf',
'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf',
'https://cdn.rive.app/runtime/flutter/send-flowers.ttf',
];
// pick a random url from the list of fonts
http.get(Uri.parse(urls[Random().nextInt(urls.length)])).then((res) {
if (mounted) {
asset.decode(
Uint8List.view(res.bodyBytes.buffer),
);
setState(() {
// force rebuild in case the Rive graphic is no longer advancing
});
}
});
return true; // Tell the runtime not to load the asset automatically
} else {
// Tell the runtime to proceed with loading the asset if it exists
return false;
}
},
);
```
Your provided callback will be passed an `asset` and `bytes`.
* `asset` - Reference to a `FileAsset` object. You can grab a number of properties from this object, such as the name, asset type, and more. You'll also use this to set a new Rive specific asset for dynamically loaded content. Types: `FontAsset`, `ImageAsset`, and `AudioAsset`.
* `bytes` - Array of bytes for the asset (if it's available as an embedded asset)
**Important**: Note that the return value is a `boolean`, which is where you need to return:
* `true` if you intend on handling and loading in an asset yourself
* or `false` if you do not want to handle asset loading for that given asset yourself, and attempt to have the runtime try to load the asset
Once the `File` is disposed, the `FileAsset` will no longer be valid and would be dangerous to use.
## Additional Resources
# Migration Guide
Source: https://rive.app/docs/runtimes/flutter/migration-guide
Learn how to migrate your Flutter app when upgrading between major versions of the Rive Flutter runtime, including breaking changes and new features.
## Version 0.14.0
This is a significant update for Rive Flutter. We've completely removed all of the Dart code that was used for the Rive runtime and replaced it with our underlying [C++ Runtime](https://github.com/rive-app/rive-runtime). See the [Rive Native for Flutter](/docs/runtimes/flutter/rive-native) page for more details.
This has resulted in a number of changes to the underlying API, and a large portion of the code base that was previously accessible through Dart is now implemented in C++ through FFI.
### What's New in 0.14.0
This release of Rive Flutter adds support for:
* [Rive Renderer](https://rive.app/renderer?utm_source=docs\&utm_medium=content)
* [Data Binding](/docs/editor/data-binding/)
* [Layouts](/docs/editor/layouts/layouts-overview)
* [Scrolling](/docs/editor/layouts/scrolling)
* [N-Slicing](/docs/editor/layouts/n-slicing)
* [Vector Feathering](https://rive.app/blog/introducing-vector-feathering?utm_source=docs\&utm_medium=content)
* All other features added to Rive that did not make it to the previous versions of Rive Flutter
* Includes the latest fixes and improvements for the Rive C++ runtime
* Adds prebuilt libraries, with the ability to [build manually](/docs/runtimes/flutter/rive-native#building-rive-native). See the [rive\_native](https://pub.dev/packages/rive_native) package for more information
* Removes the `rive_common` package and replaces it with `rive_native`
Now that Rive Flutter makes use of the core Rive C++ runtime, you can expect new Rive features to be supported sooner for Rive Flutter.
All your Rive graphics will still look and function the same as they did
before.
### Requirements
#### Dart and Flutter Versions
This release bumps to these versions:
```yaml theme={null}
sdk: ">=3.5.0 <4.0.0"
flutter: ">=3.3.0"
```
#### Required Setup
**Important:** You must call `RiveNative.init` at the start of your app, or before you use Rive. For example, in `main.dart`:
```dart theme={null}
import 'package:rive/rive.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await RiveNative.init(); // Call init before using Rive
runApp(const MyApp());
}
```
### Migration Guide
#### Quick Migration Checklist
1. ✅ Update your `pubspec.yaml` dependencies to use version `0.14.0` or later
```yaml theme={null}
dependencies:
rive: ^0.14.0-dev.8 # or latest version
```
2. ✅ Add `RiveNative.init()` to your `main()` function, or call before using Rive.
3. ✅ Replace `Rive` and `RiveAnimation` widgets with [`RiveWidget`](/docs/runtimes/flutter/flutter#rivewidget) or [`RiveWidgetBuilder`](/docs/runtimes/flutter/flutter#rivewidgetbuilder)
4. ✅ Update your controllers to use the new API, see [`RiveWidgetController`](/docs/runtimes/flutter/flutter#rivewidgetcontroller)
5. ✅ Review and update any custom asset loading code
6. ✅ Test your graphics and interactions
#### Removed Classes
The following classes have been completely removed:
* `Rive` and `RiveAnimation` widgets → Use `RiveWidget` and `RiveWidgetBuilder`
* `RiveAnimationController` and its subclasses → Use `RiveWidgetController`, `SingleAnimationPainter`, and `StateMachinePainter`
* `OneShotAnimation` and `SimpleAnimation` → Use `SingleAnimationPainter` to play individual animations
* `StateMachineController` → Use `StateMachine` instead (can be accessed via `RiveWidgetController.stateMachine`)
* `RiveEvent` → Replaced with `Event`
* `SMITrigger` → Replaced with `TriggerInput`
* `SMIBool` → Replaced with `BooleanInput`
* `SMINumber` → Replaced with `NumberInput`
* `FileAssetLoader` → Replaced with optional callback when creating a `File`
#### Loading Rive Files
`RiveFile` has been removed and replaced with `File`. Important changes:
```dart theme={null}
final file = await File.decode(bytes, factory: Factory.rive);
final artboard = file.defaultArtboard();
final artboard = file.artboard('MyArtboard');
```
```dart Old API theme={null}
final file = await RiveFile.import(bytes);
final artboard = file.mainArtboard;
final artboard = file.artboardByName('MyArtboard');
```
The provided `Factory` determines the renderer that will be used. Use `Factory.rive` for the Rive renderer or `Factory.flutter` for the shipped Flutter renderer (Skia or Impeller).
Vector Feathering only works with the Rive Renderer.
**Key Changes:**
* Creating a Rive File now requires a factory (`Factory.rive` or `Factory.flutter`)
* Replace `RiveFile.import` with `File.decode()` which returns a `Future`
* Replace `mainArtboard` with `defaultArtboard()`
* Replace `artboardByName(name)` with `artboard(name)`
* Replace `RiveFile.network` with `File.url`
* Replace `RiveFile.file` with `File.path`
#### Widget Migration
See the updated example app for a complete migration guide, including how to use the new `RiveWidget` and `RiveWidgetBuilder` APIs.
| Old Widget | New Widget | Notes |
| ---------------------- | -------------------------------- | ------------------ |
| `Rive`/`RiveAnimation` | `RiveWidget`/`RiveWidgetBuilder` | Direct replacement |
```dart Using RiveWidgetBuilder theme={null}
class SimpleAssetAnimation extends StatefulWidget {
const SimpleAssetAnimation({Key? key}) : super(key: key);
@override
State createState() => _SimpleAssetAnimationState();
}
class _SimpleAssetAnimationState extends State {
late final fileLoader = FileLoader.fromAsset(
'assets/off_road_car.riv',
riveFactory: Factory.rive,
);
@override
void dispose() {
fileLoader.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Simple Animation'),
),
body: Center(
child: RiveWidgetBuilder(
fileLoader: fileLoader,
builder: (context, state) => switch (state) {
RiveLoading() => const CircularProgressIndicator(),
RiveFailed() => Text('Failed to load: ${state.error}'),
RiveLoaded() => RiveWidget(
controller: state.controller,
fit: Fit.cover,
),
},
),
),
);
}
}
```
```dart Using RiveWidget directly theme={null}
class SimpleAssetAnimation extends StatefulWidget {
const SimpleAssetAnimation({Key? key}) : super(key: key);
@override
State createState() => _SimpleAssetAnimationState();
}
class _SimpleAssetAnimationState extends State {
File? file;
RiveWidgetController? controller;
bool isInitialized = false;
@override
void initState() {
super.initState();
initRive();
}
void initRive() async {
file = (await File.asset('assets/off_road_car.riv', riveFactory: Factory.rive))!;
controller = RiveWidgetController(file!);
setState(() => isInitialized = true);
}
@override
void dispose() {
controller?.dispose();
file?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Simple Animation'),
),
body: Center(
child: isInitialized && controller != null
? RiveWidget(
controller: controller!,
fit: Fit.cover,
)
: const CircularProgressIndicator(),
),
);
}
}
```
```dart Old API theme={null}
class SimpleAssetAnimation extends StatelessWidget {
const SimpleAssetAnimation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Simple Animation'),
),
body: const Center(
child: RiveAnimation.asset(
'assets/off_road_car.riv',
fit: BoxFit.cover,
),
),
);
}
}
```
#### Controller Migration
| Old Controller | New Controller | Notes |
| ---------------------------------------- | ------------------------ | --------------------------- |
| `RiveAnimationController` | `RiveWidgetController` | Main controller for widgets |
| `StateMachineController` | `StateMachine` | Direct state machine access |
| `OneShotAnimation` and `SimpleAnimation` | `SingleAnimationPainter` | For individual animations |
Example using the new `RiveWidgetController`:
```dart Using RiveWidgetController theme={null}
final file = await File.asset('assets/off_road_car.riv', riveFactory: Factory.rive);
final controller = RiveWidgetController(file!);
final artboard = controller.artboard; // access the loaded artboard
final viewModelInstance = controller.dataBind(DataBind.auto()); // auto data binding
```
Optionally specify which Artboard and State Machine to use:
```dart Specifying Artboard and State Machine theme={null}
final file = await File.asset('assets/off_road_car.riv', riveFactory: Factory.rive);
final controller = RiveWidgetController(
file,
artboardSelector: ArtboardSelector.byName('Main'),
stateMachineSelector: StateMachineSelector.byName('State Machine 1'),
);
```
#### Playing Animations
This functionality is deprecated. We strongly encourage playing and blending
animations through a state machine.
In the previous version you were able to play an animation directly by passing `animations: ['myAnimation']` to `RiveAnimation`.
To achieve the same in the new version, use a `SingleAnimationPainter` and `RiveArtboardWidget` instead of `RiveWidgetController` and `RiveWidget`.
```dart Single animation example expandable theme={null}
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
import 'package:rive_example/main.dart' show RiveExampleApp;
/// This is an alternative controller (painter) to use instead of the
/// [RiveWidgetController].
///
/// This painter is used to paint/advance a state machine. Functionally it's
/// very similar to the [RiveWidgetController], which we recommend using for
/// most use cases.
class ExampleSingleAnimationPainter extends StatefulWidget {
const ExampleSingleAnimationPainter({super.key});
@override
State createState() =>
_ExampleSingleAnimationPainterState();
}
class _ExampleSingleAnimationPainterState
extends State {
late File file;
Artboard? artboard;
late SingleAnimationPainter painter;
@override
void initState() {
super.initState();
init();
}
void init() async {
file = (await File.asset(
'assets/off_road_car.riv',
riveFactory: RiveExampleApp.getCurrentFactory,
))!;
painter = SingleAnimationPainter('idle');
artboard = file.defaultArtboard();
setState(() {});
}
@override
void dispose() {
painter.dispose();
artboard?.dispose();
file.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (artboard == null) {
return const Center(child: CircularProgressIndicator());
}
return RiveArtboardWidget(
artboard: artboard!,
painter: painter,
);
}
}
```
To play and mix multiple animations, you need to create your own painter. See
the implementation of `SingleAnimationPainter` and extend it to create and
advance multiple animations.
#### Handling State Machine Inputs
Consider using [Data Binding](/docs/editor/data-binding/overview) for more advanced
use cases
`StateMachineController` has been removed and replaced with `StateMachine`. Important changes:
```dart State Machine Inputs: New API theme={null}
stateMachine.trigger('myTrigger');
stateMachine.boolean('myBool');
stateMachine.number('myNumber');
```
```dart State Machine Inputs: Old API theme={null}
controller.getTriggerInput('myTrigger');
controller.getBooleanInput('myBool');
controller.getNumberInput('myNumber');
```
You can access the `stateMachine` from the `RiveWidgetController`:
```dart theme={null}
final controller = RiveWidgetController(file);
final stateMachine = controller.stateMachine;
```
It is recommended to manually dispose inputs when no longer needed:
`input.dispose()`
##### Nested Inputs
You can access nested inputs by providing an optional `path` parameter:
```dart Nested Inputs theme={null}
stateMachine.boolean('myBool', path: 'nested/path');
stateMachine.number('myNumber', path: 'nested/path');
stateMachine.trigger('myTrigger', path: 'nested/path');
```
#### Handling Rive Events
Consider using [Data Binding](/docs/editor/data-binding/overview) instead of events
for more advanced use cases.
`RiveEvent` has been removed and replaced with `Event`. `Event` is a sealed class with two options:
* `OpenUrlEvent`
* `GeneralEvent`
Registering an event listener:
```dart Rive Events: New API theme={null}
// New API
final controller = RiveWidgetController(_riveFile!);
controller?.stateMachine.addEventListener(_onRiveEvent);
void _onRiveEvent(Event event) {
// Do something with the event
}
```
```dart Rive Events: Old API theme={null}
// Old API
final controller =
StateMachineController.fromArtboard(artboard, 'State Machine 1')!;
controller.addEventListener(_onRiveEvent);
void _onRiveEvent(RiveEvent event) {
// Do something with the event
}
```
Accessing `properties` returns `Map`. `CustomProperty` is also a sealed class with options:
* `CustomNumberProperty`
* `CustomBooleanProperty`
* `CustomStringProperty`
All of these have a `value` field. On the `Event` class, there are convenient accessors:
```dart theme={null}
// Convenient accessors
event.property(name); // Returns a CustomProperty
event.numberProperty(name); // Returns a CustomNumberProperty
event.booleanProperty(name); // Returns a CustomBooleanProperty
event.stringProperty(name); // Returns a CustomStringProperty
```
#### Layout Changes
##### BoxFit → Fit
Previously we used Flutter's `BoxFit` class. Now we use our own `Fit` which includes an extra option:
```dart theme={null}
// Old API
BoxFit.contain
// New API
Fit.contain
Fit.layout // New option for layout-based fitting
```
#### Asset Loading Changes
The `FileAssetLoader` class and all its subclasses have been removed:
* `CDNAssetLoader`
* `LocalAssetLoader`
* `CallbackAssetLoader`
* `FallbackAssetLoader`
##### Out-of-band Asset Loading
Asset types: `FontAsset`, `ImageAsset`, and `AudioAsset`.
See this example that demonstrates loading random fonts.
```dart Out-of-band assets: New API theme={null}
// New API
final fontFile = await File.asset(
'assets/acqua_text_out_of_band.riv',
riveFactory: Factory.rive,
assetLoader: (asset, bytes) {
// Replace font assets that are not embedded in the rive file
if (asset is FontAsset && bytes == null) {
final urls = [
'https://cdn.rive.app/runtime/flutter/IndieFlower-Regular.ttf',
'https://cdn.rive.app/runtime/flutter/comic-neue.ttf',
'https://cdn.rive.app/runtime/flutter/inter.ttf',
'https://cdn.rive.app/runtime/flutter/inter-tight.ttf',
'https://cdn.rive.app/runtime/flutter/josefin-sans.ttf',
'https://cdn.rive.app/runtime/flutter/send-flowers.ttf',
];
// pick a random url from the list of fonts
http.get(Uri.parse(urls[Random().nextInt(urls.length)])).then((res) {
if (mounted) {
asset.decode(
Uint8List.view(res.bodyBytes.buffer),
);
setState(() {
// force rebuild in case the Rive graphic is no longer advancing
});
}
});
return true; // Tell the runtime not to load the asset automatically
} else {
return false; // Tell the runtime to proceed with loading the asset if it exists
}
},
);
```
You can also create the asset resource types manually and set them. This is useful if you want to preload the resources:
```dart theme={null}
Future updateImageAsset(ImageAsset asset, Uint8List bytes) async {
final renderImage = await Factory.rive.decodeImage(bytes);
if (renderImage != null) {
asset.renderImage(renderImage);
}
}
Future updateFontAsset(FontAsset asset, Uint8List bytes) async {
final font = await Factory.rive.decodeFont(bytes);
if (font != null) {
asset.font(font);
}
}
Future updateAudioAsset(AudioAsset asset, Uint8List bytes) async {
final audioSource = await Factory.rive.decodeAudio(bytes);
if (audioSource != null) {
asset.audio(audioSource);
}
}
```
```dart Out-of-band Asset Loading: Old API theme={null}
// Old API
assetLoader: (asset, bytes) async {
/* async work */
final someImage = await ImageAsset.parseBytes(bytes)
asset.image = someImage;
}
```
**Key Changes:**
* `assetLoader` can no longer be an asynchronous lambda
* `ImageAsset.parseBytes(bytes)` → `riveFactory.decodeImage(bytes)` or `asset.decode(bytes)`
* `FontAsset.parseBytes(bytes)` → `riveFactory.decodeFont(bytes)` or `asset.decode(bytes)`
* `AudioAsset.parseBytes(bytes)` → `riveFactory.decodeAudio(bytes)` or `asset.decode(bytes)`
* `ImageAsset.image = value` → `ImageAsset.renderImage(value)` (returns boolean)
* `FontAsset.font = value` → `FontAsset.font(value)` (returns boolean)
* `AudioAsset.audio = value` → `AudioAsset.audio(value)` (returns boolean)
#### Text Run Updates
We recommend using [Data Binding](/docs/editor/data-binding/overview) instead to
update text at runtime.
It's no longer possible to access a `TextValueRun` object directly. Use these methods instead to access the String value:
```dart Get/Set Text Run Value theme={null}
final controller = RiveWidgetController(riveFile);
final artboard = controller.artboard;
// Get a text run value
artboard.getText('textRunName')
artboard.getText('textRunName', path: 'nested/path')
// Set a text run value
artboard.setText('textRunName', 'new value')
artboard.setText('textRunName', 'new value', path: 'nested/path')
```
### Known Missing Features
These features are not available in `v0.14.0` but may be added in future releases:
* Automatic Rive CDN asset loading
* `speedMultiplier`
* `useArtboardSize`
* `clipRect`
* `isTouchScrollEnabled`
* `dynamicLibraryHelper`
### Removed Code Paths
All of the "runtime" Dart code has been removed from these paths:
* `src/controllers`
* `src/core`
* `src/generated`
* `rive_core`
* `utilities`
### Getting Help
If you encounter issues during migration:
1. Check the [Rive Flutter documentation](/docs/runtimes/flutter/flutter)
2. Review the [Data Binding guide](/docs/editor/data-binding/overview)
3. Visit the [Rive community forums](https://community.rive.app)
4. Report issues on the [GitHub repository](https://github.com/rive-app/rive-flutter)
# Playing Audio
Source: https://rive.app/docs/runtimes/flutter/playing-audio
Playing Rive audio events
To learn more on how to add audio to your Rive file, see [Audio Events](/docs/editor/events/audio-events).
On the web, most browsers restrict audio from playing until the web page is interacted with. This applies to any audio, not just Rive audio.
The web page needs to receive some interaction (touch/click) before sound is played. This interaction can be anything on the browser and doesn't need to be a Rive specific interaction.
## Embedded Assets
Embedded assets require no additional work to play audio.
## Referenced Assets
Referenced assets require a little bit more work to play audio. Audio will still automatically play, but the audio file(s) must be loaded when a Rive runtime attempts to play audio.
For more information, see [Loading Assets](/docs/runtimes/flutter/loading-assets).
# Rive Native for Flutter
Source: https://rive.app/docs/runtimes/flutter/rive-native
A Flutter plugin that integrates the Rive Renderer and the core Rive C++ runtime. Used by the Rive Flutter runtime.
## Rive Native vs Rive
[Rive Native](https://pub.dev/packages/rive_native) (`rive_native`) is a Flutter plugin that integrates the Rive Renderer and the core Rive C++ runtime.
The [Rive Flutter runtime](https://pub.dev/packages/rive) (`rive`) is built on top of `rive_native`. We recommend including the `rive` package as a dependency, as that will automatically include `rive_native`, while also providing a user-friendly API for working with Rive assets in Flutter.
Rive Native replaces the [Rive Common](https://pub.dev/packages/rive_common)
(`rive_common`) plugin that Rive Flutter previously used for native
operations.
### Understanding Rive Native
Rive Native acts as the bridge between Flutter and the Rive C++ runtime, allowing you to use Rive graphics in your Flutter applications.
* **C++ Runtime Integration**:
`rive_native` is built on Rive's [C++ runtime](https://github.com/rive-app/rive-runtime) via FFI. This ensures a consistent experience across platforms and the Rive Editor, while unlocking performance improvements and new features exclusive to the C++ runtime, such as:
* [Data Binding](/docs/editor/data-binding/)
* [Responsive Layouts](/docs/editor/layouts/)
* [Scrolling](/docs/editor/layouts/scrolling)
* [N-Slicing](/docs/editor/layouts/n-slicing)
* [Vector Feathering](https://rive.app/blog/introducing-vector-feathering)
* **Rive Renderer Support**:
`rive_native` bring the [Rive Renderer](https://rive.app/renderer?utm_source=docs\&utm_medium=content) to Flutter. While you can still use the Flutter-based renderer (Dart/Impeller), the Rive Renderer is recommended for performance-critical use cases. For more information see [Choosing a Renderer](/docs/runtimes/choose-a-renderer/overview).
Some features, like Vector Feathering, are only supported with the Rive Renderer. See the [Feature Support page](/docs/feature-support) for more details.
***
## Getting Started
`rive_native` is not yet publicly available on GitHub but will be soon. For now, you can pull the source code and example by running:
```bash theme={null}
dart pub unpack rive_native # Unpack the package source code and example app
cd rive_native/example # Navigate to the example folder
flutter create . # Create the platform folders
flutter pub get # Fetch dependencies
flutter run # Run the example app
```
For an example implementation, see the `rive_player.dart` file in `rive_native/example/rive_player.dart`.
***
## Platform Support
| Platform | Flutter Renderer | Rive Renderer |
| -------- | ---------------- | ------------- |
| iOS | ✅ | ✅ |
| Android | ✅ | ✅ |
| macOS | ✅ | ✅ |
| Windows | ✅ | ✅ |
| Linux | ✅ | ✅ |
| Web | ✅ | ✅ |
Prebuilt Linux libraries do not yet include arm64. On Linux arm64, [build
`rive_native` manually](#building-rive-native).
***
## Feature Support
See the [Feature Support page](/docs/feature-support) for details.
***
## Troubleshooting
The required native libraries should be automatically downloaded during the build step (`flutter run` or `flutter build`). If you encounter issues, try the following:
1. Run `flutter clean`
2. Run `flutter pub get`
3. Run `flutter run`
Alternatively, you can manually run the `rive_native` setup script. In the root of your Flutter app, execute:
```bash theme={null}
dart run rive_native:setup --verbose --clean --platform macos
```
This will clean the `rive_native` setup and download the platform-specific libraries specified with the `--platform` flag. Refer to the **Platform Support** section above for details.
### Android
If you're running into automated setup issues (example issues [555](https://github.com/rive-app/rive-flutter/issues/555) and [515](https://github.com/rive-app/rive-flutter/issues/515)),
you can skip setup by setting `rive.native.skipSetup=true` in your app's `gradle.properties`.
When enabled, you must manually run `dart run rive_native:setup --verbose --clean --platform android` to download the required libraries.
***
## Building `rive_native`
By default, prebuilt native libraries are downloaded and used. You can build them from source instead: the published `rive_native` package includes the full C++ source (`native/` and `runtime/`), and the setup script compiles it in place in the Pub cache.
Check the [build requirements](#build-requirements) for your platform, then run the following in the root of your Flutter app:
```bash theme={null}
flutter clean # Important
flutter pub get # Setup needs .dart_tool/package_config.json
dart run rive_native:setup --verbose --clean --build --platform macos
```
`--platform` accepts `android`, `ios`, `macos`, `windows`, and `linux`, or a comma-separated list (for example, `android,macos`). Each platform except Android must be built on the operating system it targets; Android builds on any host and produces all four ABIs (`armeabi-v7a`, `arm64-v8a`, `x86`, `x86_64`). For web, see [Web](#web) below.
A successful build leaves a `rive_marker__development` file in the package, so subsequent `flutter run` and `flutter build` calls use your locally built libraries instead of downloading prebuilts. Without `--clean`, setup does nothing once this marker exists.
### Build Requirements
All platforms:
* Network access and `git`: the first build clones the build system and third-party sources (HarfBuzz, Yoga, zlib, and others) from public GitHub repositories into the package's `native/dependencies/` folder - no credentials required.
* `bash`, GNU `make`, and `python3`: used by the build scripts and shader generation.
Per platform:
* **macOS / iOS**: Xcode
* **Android**: Android NDK r27c (`27.2.12479018`) - the exact version is enforced; point `NDK_PATH` or `ANDROID_NDK` at it - and `ninja`.
* **Windows**: Visual Studio 2022 (**Desktop development with C++** workload) with `msbuild.exe` on `PATH` (for example, run from a Visual Studio developer prompt), Git Bash, and `make` (`choco install make`).
* **Linux**: `clang`, `cmake`, `ninja-build`, `pkg-config`, `libgtk-3-dev`, `uuid-dev`, `libstdc++-12-dev`, and `libvulkan-dev` (apt package names).
### Returning to Prebuilt Libraries
Run the setup script without `--build` - `--clean` removes the built libraries and marker files, and prebuilts are downloaded again:
```bash theme={null}
dart run rive_native:setup --verbose --clean --platform macos
flutter clean
```
Because downloaded and locally built libraries both live inside the Pub cache, `dart pub cache repair` and `dart pub cache clean` also remove them. They are restored on the next `flutter run`, or by re-running the setup script.
***
## Web
The setup script does not manage web binaries. On web, `rive_native` loads its WebAssembly module at runtime from Rive's CDN ([jsDelivr](https://www.jsdelivr.com/package/npm/@rive-app/flutter-native-wasm), pinned to the version matching the package).
To self-host these files instead, copy the `wasm/` and `wasm_compatibility/` folders from the [`@rive-app/flutter-native-wasm`](https://www.npmjs.com/package/@rive-app/flutter-native-wasm) NPM package to your server, then build with:
```bash theme={null}
flutter run --dart-define=RIVE_NATIVE_WASM_HOST=https://your-host/your-path/
```
The trailing slash is required - the runtime appends `wasm/rive_native.js` or `wasm_compatibility/rive_native.js` to the host URL.
***
## Testing
Shared libraries are included in the download/build process. If you encounter issues using `rive_native` in your tests, please reach out to us for assistance.
# State Machine Playback
Source: https://rive.app/docs/runtimes/flutter/state-machines
Playing a state machine
For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/docs/editor/state-machine).
A Rive state machine is a set of animation states and the transitions between them. At runtime there is limited ability to observe or modify the state directly. This is by design, as this would limit the ability of a designer in Rive to modify the state machine without creating breaking changes. Instead, state machines are indirectly controlled through transitions conditioned on Data Binding properties.
A designer assigns a default state machine for each artboard in the Rive editor. They may create multiple state machines, each representing a different configuration of states and transitions. When rendering a Rive file and artboard, you may choose which state machine to play. If no state machine is specified, the default state machine for that artboard is used.
## Controlling Playback
State machines play by "advancing" over time. This is done once per frame by the amount of time between frames. For example, for a graphic running at 60 frames per second, the state machine would be advanced by approximately 16.67 milliseconds (1/60th of a second) each frame. This advancing evaluates keyframes, transitions, data bindings changes, and ultimately the visible artboard elements to create the illusion of motion over time.
This runtime provides a way to control whether the state machine is playing. When paused or stopped, the state machine does not advance and the last rendered frame remains visible. When playing from pause, the state machine resumes from where it left off, whereas when playing from stop, it restarts from the entry state.
In addition to the paused/stopped state, state machines may also "settle". This is an optimization where the Rive runtime detects that no further changes will occur (for example, if there are no active transitions or animations). While settled the state machine will also stop advancing. This improves performance and energy use by avoiding unnecessary calculations. State machines are unsettled by external actions that change their state, such as user input or data binding changes. You can additionally force a state machine to unsettle by calling play, though it may immediately re-settle if there is no further work to be done.
## Playing State Machines
There are a number of ways to play/select a state machine in Flutter.
#### When Using `RiveWidgetController` (Recommended)
When you create a `RiveWidgetController` it will use the default state machine, or you can specify a state machine by name or index.
```dart theme={null}
// Default state machine
var controller = RiveWidgetController(riveFile);
// By name
controller = RiveWidgetController(
riveFile,
stateMachineSelector: StateMachineSelector.byName("State Machine 1"),
);
// By index
controller = RiveWidgetController(
riveFile,
stateMachineSelector: StateMachineSelector.byIndex(0),
);
```
Passing this controller to a `RiveWidget` will automatically play the state machine.
```dart theme={null}
@override
Widget build(BuildContext context) {
return RiveWidget(controller: controller);
}
```
You can mark the controller as `active` to play/pause the state machine (advancing and drawing):
```dart theme={null}
final controller = RiveWidgetController(riveFile);
controller.active = false;
```
The `StateMachineSelector` can also be passed to `RiveWidgetBuilder` to specify which state machine to use:
```dart theme={null}
return RiveWidgetBuilder(
fileLoader: fileLoader,
stateMachineSelector: StateMachineSelector.byIndex(0),
builder: (context, state) => switch (state) {
/// ...
},
);
```
#### When Using `StateMachinePainter`
When using `StateMachinePainter`, you can specify the state machine to use by passing an optional name.
```dart theme={null}
// Default state machine
final painter = rive.StateMachinePainter(withStateMachine: _withStateMachine);
// By name
painter = rive.StateMachinePainter(
withStateMachine: _withStateMachine,
stateMachineName: 'State Machine 1 ',
);
```
#### Creating a State Machine Directly
Create the state machine directly from an `Artboard`:
```dart theme={null}
final artboard = riveFile.defaultArtboard()!;
// Default state machine
var stateMachine = artboard.defaultStateMachine();
// By name
stateMachine = artboard.stateMachine('State Machine 1');
// By index
stateMachine = artboard.stateMachineAt(0);
```
# Getting Started with the Rive Runtimes
Source: https://rive.app/docs/runtimes/getting-started
Run Rive on your platform of choice.
The Rive runtimes are open-source libraries that allow you to load and control your animations in apps, games, and websites. Dive into each of the subpages to get started!
Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.
For more details, refer to the [feature support](/docs/feature-support/) and [choosing a renderer](/docs/runtimes/choose-a-renderer/) pages.
## How to use this guide
In this section you'll find runtime subpages that provide all the needed information and resources to get started on your platform of choice. See [Installation and getting started](#installation-and-getting-started) below.
You'll also find pages dedicated to controlling your Rive graphics at runtime. For example, updating data bound properties and loading in assets out-of-band. See [Graphic control and interaction](#graphic-control-and-interaction) below.
### Installation and getting started
Make sure to check out the additional documentation provided under each runtime section. These documents provide platform-specific considerations, migration guides, and advanced usage information.
}>
This guide documents how to get started using the Web runtime library.
}>
This guide documents how to get started using the React runtime library.
}>
This guide documents how to get started using the React Native runtime library.
}>
This guide documents how to get started using the Apple runtime library.
}>
This guide documents how to get started using the Android runtime library.
}>
This guide documents how to get started using the Flutter runtime library.
}>
This guide documents how to get started using the Unity runtime library.
}>
This guide documents how to get started using the Unreal runtime library.
## Other sections
}>
Specify the desired renderer to use at runtime. Each runtime provides different options. We recommend using the Rive Renderer.
}>
The Rive File format.
}>
Runtime support for Rive features.
## Versioning
As we publish updates to our Rive editor, we will occasionally push updated runtimes to support the new features. See [Feature Support](/docs/feature-support) for the required minimum runtime version needed for specific features.
In most cases, the newest runtimes will also support previous versions of your Rive assets, so you will not need to re-export assets to update to the latest runtimes.
There are a number of ways to export your Rive files in cases where re-exporting is necessary to take advantage of the latest features. Check out our documentation on [Exporting](/docs/editor/exporting) for more information.
## Official runtimes
Check out the runtime subpages for steps on how to get started!
All web runtimes are distributed via npm:
* [GitHub](https://github.com/rive-app/rive-wasm)
* [canvas](https://www.npmjs.com/package/@rive-app/canvas)
* [webgl2](https://www.npmjs.com/package/@rive-app/webgl2)
* [canvas-lite](https://www.npmjs.com/package/@rive-app/canvas-lite)
**See [Canvas vs WebGL](/docs/runtimes/web/canvas-vs-webgl) for package guidance and sizing tradeoffs.**
All React runtimes are distributed via npm:
* [GitHub](https://github.com/rive-app/rive-react)
* [canvas](https://www.npmjs.com/package/@rive-app/react-canvas)
* [canvas-lite](https://www.npmjs.com/package/@rive-app/react-canvas-lite)
* [webgl2](https://www.npmjs.com/package/@rive-app/react-webgl2)
The Apple runtime is distributed by:
* [Swift Package Manager](https://swiftpackageregistry.com/rive-app/rive-ios)
* Cocoapods
[GitHub](https://github.com/rive-app/rive-ios)
* [Maven](https://search.maven.org/artifact/app.rive/rive-android)
* [GitHub](https://github.com/rive-app/rive-android)
* [pub.dev](https://pub.dev/packages/rive)
* [GitHub](https://github.com/rive-app/rive-flutter)
* [GitHub](https://github.com/rive-app/rive-cpp)
* [UWP (Recommended)](https://dev.azure.com/dotnet/CommunityToolkit/_artifacts/feed/CommunityToolkit-Labs/NuGet/CommunityToolkit.Labs.Uwp.RivePlayer/overview/0.0.1)
* [WinUI](https://dev.azure.com/dotnet/CommunityToolkit/_artifacts/feed/CommunityToolkit-Labs/NuGet/CommunityToolkit.Labs.WinUI.RivePlayer/overview/0.0.1)
* (High-level API) [RivePlayer Github](https://github.com/CommunityToolkit/Labs-Windows/blob/main/labs/RivePlayer/samples/RivePlayer.Samples/RivePlayer.md)
* (Low-level API) [RiveSharp Github](https://github.com/rive-app/rive-sharp)
**High-level APIs**:
* [WinUI (High-level)](https://dev.azure.com/dotnet/CommunityToolkit/_artifacts/feed/CommunityToolkit-Labs/NuGet/CommunityToolkit.Labs.WinUI.RivePlayer/overview/0.0.1)
* [npm](https://www.npmjs.com/package/rive-react-native)
* [GitHub](https://github.com/rive-app/rive-react-native)
## Community runtimes
| **Runtime** | **Author** | **Link** |
| ----------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| QtQuick | [basysKom](https://github.com/basysKom) | [Github](https://github.com/basysKom/RiveQtQuickPlugin) |
| UWP (C#) | Windows Community Toolkit | [Github](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/RivePlayer/samples/RivePlayer.md) |
| RiveCMP | [muazkadan](https://github.com/muazkadan) | [Github](https://github.com/muazkadan/Rive-CMP/blob/main/README.md) |
## Handling .riv files
When checking in `.riv` files with Git, consider adding a `.gitattributes` file and marking `.riv` files as `binary` files to prevent Git from changing line endings when these files are checked in. Otherwise, some platforms may accidentally corrupt the `.riv` file where there are line returns (i.e. Windows CRLF line endings vs LF line endings) and cause issues at runtime when the file is read.
```riv theme={null}
.gitattributes
*.riv binary
```
## Licensing
Our official runtimes are all open-source and licensed under the [MIT License](https://choosealicense.com/licenses/mit/). You're free to use them for personal and commercial applications.
## Contributing
Since all the runtimes are open-source, we encourage you to dive in and take a look around! If you see something missing or feel you can improve upon it, then fork it!
# Adding Rive to Expo
Source: https://rive.app/docs/runtimes/react-native/adding-rive-to-expo
Rive React Native Expo.
To use Rive with Expo, you'll need to install the Rive React Native package.
Because this package contains custom native code, it's not compatible with Expo Go. Instead, you'll need to use a development build, which gives you full access to native modules.
Development builds are the [recommended setup for production
apps](https://github.com/expo/fyi/blob/main/expo-go-usage.md).
This guide will walk you through integrating Rive into your Expo project, including installing dependencies, configuring your build, and testing your graphics.
## Initial Setup
If you don’t already have a project, create a new Expo app:
```bash theme={null}
npx create-expo-app MyRiveApp
```
Install the Expo development client:
```bash theme={null}
npx expo install expo-dev-client
```
Then install the Rive package:
```bash theme={null}
npx expo install @rive-app/react-native
```
```bash theme={null}
npx expo install rive-react-native
```
## Android - Expo SDK 53
Expo SDK 53 may fail to build on Android due to dependency version conflicts. The Rive Android SDK requires `compileSdkVersion` 36 and Android Gradle Plugin 8.9.1+, but Expo SDK 53 defaults to lower versions.
To fix this, install `expo-build-properties` and `expo-custom-agp`:
```bash theme={null}
npx expo install expo-build-properties expo-custom-agp
```
Then update your `app.json` or `app.config.js`:
```json theme={null}
{
"expo": {
"plugins": [
["expo-custom-agp", "8.9.2"],
[
"expo-build-properties",
{
"android": {
"compileSdkVersion": 36
}
}
]
]
}
}
```
After updating, run `npx expo prebuild --clean` and rebuild your app.
## iOS Minimum Version
The new runtime requires iOS **15.1** or later.
If you’re using Expo SDK 52 or later, it already requires `15.1` or later.
If you're using an older SDK, you’ll need to update your iOS deployment target manually or via configuration.
### Option 1: Using `expo-build-properties` (Recommended)
[Continuous Native Generation (CNG)](https://docs.expo.dev/workflow/continuous-native-generation/) simplifies app maintenance and configuration by automatically generating your iOS and Android native projects using [Prebuild](https://docs.expo.dev/workflow/continuous-native-generation/#usage).
If you're using CNG, you can set the minimum iOS deployment target directly in your `app.json` or `app.config.js`:
```json theme={null}
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "15.1"
}
}
]
]
}
}
```
### Option 2: Manual Configuration
If you’re not using Prebuild, update the target directly in your `ios/Podfile`:
```ruby theme={null}
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
```
The legacy runtime requires iOS **14.0** or later.
If you're using Expo SDK 52 or later, you can skip this step as it has a higher default.
If you're using an older SDK, you'll need to update your iOS deployment target manually or via configuration.
### Option 1: Using `expo-build-properties` (Recommended)
[Continuous Native Generation (CNG)](https://docs.expo.dev/workflow/continuous-native-generation/) simplifies app maintenance and configuration by automatically generating your iOS and Android native projects using [Prebuild](https://docs.expo.dev/workflow/continuous-native-generation/#usage).
If you're using CNG, you can set the minimum iOS deployment target directly in your `app.json` or `app.config.js`:
```json theme={null}
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "14.0"
}
}
]
]
}
}
```
### Option 2: Manual Configuration
If you're not using Prebuild, update the target directly in your `ios/Podfile`:
```ruby theme={null}
platform :ios, podfile_properties['ios.deploymentTarget'] || '14.0'
```
## Creating a Development Build
To run your app with the Rive runtime, you’ll need to create a development build.
Since there are several ways to do this, refer to the [Expo development builds guide](https://docs.expo.dev/develop/development-builds/create-a-build/) to choose the method that best suits your needs.
## Running Your App
Once you've created a development build and installed it on your device or simulator, start your app with:
```bash theme={null}
npx expo start
```
You can use the following component to test Rive:
```tsx theme={null}
import { View, ActivityIndicator, Text } from "react-native";
import { RiveView, useRiveFile, Fit } from "@rive-app/react-native";
export default function RiveDemo() {
const { riveFile, isLoading, error } = useRiveFile(
"https://public.rive.app/community/runtime-files/2195-4346-avatar-pack-use-case.riv"
);
if (isLoading) {
return (
);
}
if (error || !riveFile) {
return (
Error loading Rive file: {error || "Unknown error"}
);
}
return (
);
}
```
If you encounter errors loading the Rive file, make sure you're running in a development build and not Expo Go.
```js theme={null}
import { View } from "react-native";
import Rive from "rive-react-native";
export default function RiveDemo() {
return (
);
}
```
If you encounter this error: `Invariant Violation: requireNativeComponent:
"RiveReactNativeView" was not found in the UIManager`, it usually means the
app is running in **Expo Go**. Press `s` in your terminal and select the
development build instead.
## Adding Local Assets
The example above loads a `.riv` file from a remote URL.
To use local `.riv` files, they must be bundled into your native build.
See [Loading in Rive Files](/docs/runtimes/react-native/loading-rive-files) for instructions on working with local assets.
# Artboards
Source: https://rive.app/docs/runtimes/react-native/artboards
Selecting which artboard to render at runtime
For more information on creating artboards in the Rive editor, please refer to [Artboards](/docs/editor/fundamentals/artboards).
## Choosing an Artboard
When a Rive object is instantiated or when a Rive file is rendered, you can specify the artboard to use. If no artboard is given, the [default artboard](/docs/editor/fundamentals/artboards#default-state-machine), as set in the Rive editor, is used. If no default artboard is set, the first artboard is used.
Only one artboard can be rendered at a time.
```javascript theme={null}
export default function App() {
return (
);
}
```
# Caching a Rive File
Source: https://rive.app/docs/runtimes/react-native/caching-a-rive-file
Under most circumstances a `.riv` file should load quickly and managing the `RiveFile` yourself is not necessary. But if you intend to use the same `.riv` file in multiple parts of your application, or even on the same screen, it might be advantageous to load the file once and keep it in memory.
## Example Usage
In the new React Native runtime, you always need to load and manage the lifetime of a `RiveFile` object that is passed to `RiveView`. The `useRiveFile` hook handles loading, and you can reuse the same `RiveFile` across multiple `RiveView` components to cache it in memory.
Here's an example showing how to cache a Rive file and reuse it across multiple components:
```tsx Reuse RiveFile example expandable theme={null}
import { useState } from 'react';
import { View, ActivityIndicator, Text } from 'react-native';
import {
RiveView,
useRiveFile,
Fit,
type RiveFile,
} from '@rive-app/react-native';
// Custom component to display a Rive animation
const RiveExample = ({ riveFile }: { riveFile: RiveFile }) => {
return (
);
};
export default function CacheExample() {
// Load the Rive file once using useRiveFile
const { riveFile, isLoading, error } = useRiveFile(
require('../../assets/rive/rating.riv')
);
const [instanceCount] = useState(5); // Number of RiveExample components to render
if (isLoading) {
return ;
}
if (error || !riveFile) {
return Failed to load Rive file: {error || 'Unknown error'};
}
// Each RiveExample component uses the same RiveFile we loaded earlier,
// so it is only fetched and initialized once
return (
{Array.from({ length: instanceCount }, (_, index) => (
))}
);
}
```
To optimize memory usage, reuse the same `RiveFile` object across multiple `RiveView` instances if they use the same `.riv` file. This ensures the file is loaded only once and shared in memory.
#### Managing State
How you keep the `RiveFile` alive and share it with components depends on your state management approach:
* **Global access**: Load the file at the app level or in a context provider, and expose it using React Context or a state management library like Redux or Zustand.
* **Component-level**: If the file is only needed in a specific part of your app, load it in a parent component and pass it down as props.
* **Custom hook**: Create a custom hook that manages the `RiveFile` lifecycle and provides it to consuming components.
#### Memory Management
The `useRiveFile` hook automatically manages the lifecycle of the `RiveFile` object. When the component unmounts or the input changes, the hook will dispose of the previous file and load a new one if needed. This gives you automatic memory management without manual cleanup.
#### Network Assets
To load a Rive file from a remote URL, pass the URL string to `useRiveFile`:
```tsx theme={null}
const { riveFile, isLoading, error } = useRiveFile(
'https://cdn.rive.app/animations/vehicles.riv'
);
```
For network assets, caching the file in memory avoids repeated downloads and unnecessary decoding. The `useRiveFile` hook handles this automatically as long as you reuse the same `riveFile` object.
See [Loading Rive Files](/docs/runtimes/react-native/loading-rive-files) for more information on different loading methods.
Not supported
# Data Binding
Source: https://rive.app/docs/runtimes/react-native/data-binding
Connect your code to bound editor elements using View Models
Before engaging with the runtime data binding APIs, it is important to familiarize yourself with the core concepts presented in the [Overview](/docs/editor/data-binding/overview).
# 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](#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.
```tsx theme={null}
import { useRiveFile } from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
// Get reference by name
const namedVM = await riveFile?.viewModelByNameAsync('My View Model');
// Get all view model names
const vmNames = await riveFile?.getViewModelNamesAsync(); // ['My View Model', ...]
// Get reference to the default artboard view model
const defaultVM = await riveFile?.defaultArtboardViewModelAsync();
```
Creating a view model object is only supported in the new React Native runtime.
# 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:
| Type | Value |
| ----------------- | --------------- |
| Number | 0 |
| String | Empty string |
| Boolean | False |
| Color | 0xFF000000 |
| Trigger | Untriggered |
| Enum | The first value |
| Image | No image |
| Font | No font |
| Artboard | No artboard |
| List | Empty list |
| Nested view model | Null |
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.
In some samples, due to the wordiness of "view model instance", we use the abbreviation "VMI", as well as "VM" for "view model".
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 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 theme={null}
import { Text } from 'react-native';
import { useRiveFile, useViewModelInstance, RiveView } from '@rive-app/react-native';
const { riveFile, error: fileError } = useRiveFile(require('./my_file.riv'));
// From RiveFile — default artboard's ViewModel, default instance
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 theme={null}
// Specify artboard or ViewModel name (mutually exclusive)
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' });
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, 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, 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, isLoading, error } = useViewModelInstance(riveFile, { async: true, required: true });
// With onInit to set initial values before the instance is exposed or bound
const { instance, isLoading, error } = useViewModelInstance(riveFile, {
async: true,
onInit: (vmi) => {
vmi.numberProperty('health')?.set(100);
},
});
```
`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:
```tsx theme={null}
import { useRive, useViewModelInstance } from '@rive-app/react-native';
const { riveViewRef, setHybridRef } = useRive();
const { instance, isLoading, error } = useViewModelInstance(riveViewRef, { async: true });
```
You can bind a view model instance to a Rive component by passing in a `dataBinding` prop to the Rive component.
The `dataBinding` prop accepts a `DataBindBy` type, which can be one of the following:
```typescript theme={null}
export type DataBindBy =
| { type: 'autobind'; value: boolean }
| { type: 'index'; value: number }
| { type: 'name'; value: string }
| { type: 'empty' };
export const AutoBind = (value: boolean): DataBindBy => ({
type: 'autobind',
value,
});
export const BindByIndex = (value: number): DataBindBy => ({
type: 'index',
value,
});
export const BindByName = (value: string): DataBindBy => ({
type: 'name',
value,
});
export const BindEmpty = (): DataBindBy => ({ type: 'empty' });
```
Example usage:
```typescript {7,8,9,10} theme={null}
const [setRiveRef, riveRef] = useRive();
return (
);
```
You can listen to errors by passing in the `onError={(riveError: RNRiveError)` prop to the Rive component.
The `riveError` object contains the error type and message, and you can filter out for `RNRiveErrorType.DataBindingError`:
```typescript theme={null}
onError={(riveError: RNRiveError) => {
switch (riveError.type) {
case RNRiveErrorType.DataBindingError: {
console.error(`${riveError.message}`);
return;
}
default:
console.error('Unhandled error');
return;
}
}}
```
### Binding
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.
For React Native, no additional steps are needed to bind the view model instance to the Rive component. Pass the instance to the `dataBind` prop on `RiveView`:
```tsx theme={null}
import { RiveView, useRiveFile, useViewModelInstance } from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
return (
);
```
For React Native, no additional steps are needed to bind the view model instance to the Rive component. The `dataBinding` prop handles this automatically.
### 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.
Auto-binding is available through the `DataBindMode` enum. You can pass `DataBindMode.Auto` to the `dataBind` prop:
```tsx theme={null}
import { RiveView, useRiveFile, DataBindMode } from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
return (
);
```
You can also bind by name:
```tsx theme={null}
```
Or bind a specific instance:
```tsx theme={null}
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
```
The default value for the `dataBinding` prop is `AutoBind(false)`, which means auto-binding is disabled by default.
To enable auto-binding, set the `dataBinding` prop to `AutoBind(true)`.
```typescript {7} theme={null}
const [setRiveRef, riveRef] = useRive();
return (
);
```
# 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:
| Type | Supported |
| ---------------------- | --------- |
| Floating point numbers | ✅ |
| Booleans | ✅ |
| Triggers | ✅ |
| Strings | ✅ |
| Enumerations | ✅ |
| Colors | ✅ |
| Nested View Models | ✅ |
| Lists | ✅ |
| Images | ✅ |
| Artboards | ✅ |
For more information on version compatibility, see the [Feature Support](/docs/feature-support) page.
### Listing Properties
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.
Coming soon
The properties API is not supported on the legacy runtime.
### Reading and Writing 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.
Use the specific hooks for each property type to get and set property values:
* `useRiveBoolean`: Read/write boolean properties
* `useRiveString`: Read/write string properties
* `useRiveNumber`: Read/write number properties
* `useRiveColor`: Read/write color properties with hex string or RGBA support
* `useRiveEnum`: Read/write enum properties
* `useRiveTrigger`: Fire trigger events with optional callbacks
These hooks return the current `value`, a setter function (`setValue` or `trigger`), and an `error` if the property is not found.
The `setValue` function allows you to pass a function that receives the previous value, similar to React's `setState` pattern. This is useful when you need to update a value based on its current state:
```tsx theme={null}
setValue((v) => (v ?? 0) + 5)
```
```tsx theme={null}
import {
useRiveFile,
useViewModelInstance,
useRiveBoolean,
useRiveString,
useRiveNumber,
useRiveColor,
useRiveEnum,
useRiveTrigger,
RiveView
} from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
// Boolean
const { value: isActive, setValue: setIsActive, error: boolError } = useRiveBoolean(
'isToggleOn',
instance
);
// Set: setIsActive(true);
// String
const { value: userName, setValue: setUserName, error: stringError } = useRiveString(
'user/name',
instance
);
// Set: setUserName('Rive');
// Number
const { value: score, setValue: setScore, error: numberError } = useRiveNumber(
'levelScore',
instance
);
// Set: setScore(100);
// Color (accepts hex string or RiveColor object)
const { value: themeColor, setValue: setThemeColor, error: colorError } = useRiveColor(
'ui/themeColor',
instance
);
// Set: setThemeColor('#FF0000FF'); // hex string
// Or: setThemeColor({ r: 255, g: 0, b: 0, a: 255 }); // RGBA object
// Enum
const { value: status, setValue: setStatus, error: enumError } = useRiveEnum(
'appStatus',
instance
);
// Set: setStatus('loading');
// Trigger (No value, just a trigger function)
const { trigger: playEffect, error: triggerError } = useRiveTrigger(
'playButtonEffect',
instance,
{
// Optional callback to be called when the trigger is fired
onTrigger: () => {
console.log('Trigger Fired!');
}
}
);
// Trigger: playEffect();
return (
);
```
The `value` returned by each hook will update automatically when the property changes in the Rive graphic.
The following data binding methods are exposed on the `RiveRef` object.
```typescript theme={null}
setBoolean: (path: string, value: boolean) => void;
setString: (path: string, value: string) => void;
setNumber: (path: string, value: number) => void;
setColor: (path: string, color: RiveRGBA | string) => void;
setEnum: (path: string, value: string) => void;
trigger: (path: string) => void;
```
The color property can be set using either a `RiveRGBA` object or a hex string. The hex string should be in the format
`#RRGGBBAA`, where `RR`, `GG`, `BB`, and `AA` are two-digit hexadecimal values representing the red, green, blue, and
alpha channels, respectively.
```js theme={null}
type RiveRGBA = { r: number; g: number; b: number; a: number };
```
Example usage:
```typescript theme={null}
const [setRiveRef, riveRef] = useRive();
const setBoolean = () => {
if (riveRef) {
riveRef.setBoolean('My Boolean Property', true);
}
};
const setString = () => {
if (riveRef) {
riveRef.current.setString('My String Property', 'Hello, Rive');
}
};
const setNumber = () => {
if (riveRef) {
riveRef.current.setNumber('My Number Property', 10);
}
};
const setColor = () => {
if (riveRef) {
riveRef.setColor('My Color Property', { r: 255, g: 0, b: 0, a: 1 });
// or
riveRef.setColor('My Color Property', '#00FF00FF');
}
};
const setEnum = () => {
if (riveRef) {
riveRef.setEnum('My Enum Property', 'Option 1');
}
};
const trigger = () => {
if (riveRef) {
riveRef.trigger('My Trigger Property');
}
};
```
### 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.
Access nested properties by providing the full path (separated by `/`) as the first argument to the property hooks.
```tsx theme={null}
import { useRiveString, useRiveNumber, useRiveFile, useViewModelInstance } from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
// Accessing 'settings/theme/name' (String)
const { value: themeName, setValue: setThemeName } = useRiveString(
'settings/theme/name',
instance
);
// Accessing 'settings/volume' (Number)
const { value: volume, setValue: setVolume } = useRiveNumber(
'settings/volume',
instance
);
console.log('Current theme:', themeName);
// setThemeName('Dark Mode');
// setVolume(80);
```
The Rive React Native runtime does not yet support accessing a ViewModel property directly, to be able to do the chain notation.
The Rive React Native runtime does not support accessing nested properties using the chain notation.
But you can access nested properties using the path notation.
```js theme={null}
const [setRiveRef, riveRef] = useRive();
const nestedNumberByPath = useRiveNumber(riveRef, 'My Nested View Model/My Second Nested VM/My Nested Number');
useEffect(() => {
if (nestedNumberByPath) {
nestedNumberByPath.setValue(10);
}
}, [nestedNumberByPath]);
```
### 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.
Values are observed automatically through hooks. When a property's value changes within the Rive instance (either because you set it via a hook or due to an internal binding), the `value` returned by the corresponding hook updates. This state change triggers a re-render of your React component, allowing you to react to the new value.
For Triggers, you can provide an `onTrigger` callback directly to the `useRiveTrigger` hook, which fires when the trigger is activated in the Rive instance.
```tsx theme={null}
import {
useRiveFile,
useViewModelInstance,
useRiveBoolean,
useRiveString,
useRiveNumber,
useRiveColor,
useRiveEnum,
useRiveTrigger,
useEffect
} from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: boolValue, setValue: setBoolValue } = useRiveBoolean('My Boolean Property', instance);
const { value: stringValue, setValue: setStringValue } = useRiveString('My String Property', instance);
const { value: numberValue, setValue: setNumberValue } = useRiveNumber('My Number Property', instance);
const { value: colorValue, setValue: setColorValue } = useRiveColor('My Color Property', instance);
const { value: enumValue, setValue: setEnumValue } = useRiveEnum('My Enum Property', instance);
const { trigger: triggerButton } = useRiveTrigger('My Trigger Property', instance, {
onTrigger: () => {
console.log('Trigger fired');
}
});
useEffect(() => {
if (numberValue !== undefined) {
console.log('numberValue changed:', numberValue);
}
}, [numberValue]);
const handleButtonPress = () => {
triggerButton();
};
```
The `useRiveTrigger` hook returns a `trigger` function that can be called to fire the trigger. This hook accepts an optional `onTrigger` callback in its third parameter that will be executed when the trigger is fired.
Values are observed through hooks.
```typescript theme={null}
const [setRiveRef, riveRef] = useRive();
const [boolValue, setBoolValue] = useRiveBoolean(riveRef, 'My Boolean Property');
const [stringValue, setStringValue] = useRiveString(riveRef, 'My String Property');
const [numberValue, setNumberValue] = useRiveNumber(riveRef, 'My Number Property');
const [colorValue, setColorValue] = useRiveColor(riveRef, 'My Color Property');
const [enumValue, setEnumValue] = useRiveEnum(riveRef, 'My Enum Property');
const triggerButton = useRiveTrigger(riveRef, 'My Trigger Property', () => {
console.log('Trigger fired');
});
useEffect(() => {
if (numberValue !== undefined) {
console.log('numberValue changed:', numberValue);
}
}, [numberValue]);
const handleButtonPress = () => {
if (triggerButton) {
triggerButton();
}
};
```
The `useRiveTrigger` hook does not return a value, but instead takes a callback function as its third argument.
This callback will be executed when the trigger is fired.
### Images
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.
Image properties can be set using the `imageProperty` method on a `ViewModelInstance` and the `RiveImages` utility for loading images.
```tsx theme={null}
import {
useRive,
useRiveFile,
useViewModelInstance,
RiveView,
RiveImages,
type RiveViewRef
} from '@rive-app/react-native';
import { useRef } from 'react';
const { riveViewRef, setHybridRef } = useRive();
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const riveViewRef = useRef(undefined);
const handleLoadImage = async () => {
if (!riveViewRef) return;
const vmi = riveViewRef.getViewModelInstance();
if (!vmi) return;
const imgProp = vmi.imageProperty('imageValue');
if (!imgProp) return;
// Load image from URL
const riveImage = await RiveImages.loadFromURLAsync(
'https://picsum.photos/id/372/500/500'
);
imgProp.set(riveImage);
riveViewRef.playIfNeeded();
};
return (
);
```
You can also add listeners to image properties:
```tsx theme={null}
const imgProp = vmi.imageProperty('imageValue');
if (imgProp) {
imgProp.addListener(() => {
console.log('Image property changed!');
});
}
```
Other image loading options on `RiveImages`:
```ts theme={null}
/**
* Load an image from a bundled resource
* @param resource The resource name (e.g., "image.png")
* @returns A promise that resolves to the loaded RiveImage
*/
loadFromResourceAsync(resource: string): Promise;
/**
* Load an image from raw bytes
* @param bytes The image data as an ArrayBuffer
* @returns A promise that resolves to the loaded RiveImage
*/
loadFromBytesAsync(bytes: ArrayBuffer): Promise;
```
Image data binding is not supported on the legacy runtime.
### Lists
List properties let you manage a dynamic set of view model instances at runtime. For example, you can build a to-do app where users can add and remove tasks in a scrollable Layout.
See the [Editor section](/docs/editor/data-binding/lists) on creating data bound lists.
A single list property can include different view model types, with each view model tied to its own Component, making it easy to populate a list with a variety of Component instances.
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](/docs/editor/data-binding/lists#view-model-list-property) editor documentation.
Use the `useRiveList` hook to manage list properties on view model instances.
```tsx theme={null}
import {
useRiveFile,
useViewModelInstance,
useRiveList,
RiveView
} from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
// Get the list property with manipulation functions
const {
length,
getInstanceAt,
addInstance,
addInstanceAt,
removeInstance,
removeInstanceAt,
swap,
error
} = useRiveList('todos', instance);
// Add a new todo item
const handleAddItem = async () => {
const todoItemViewModel = await riveFile?.viewModelByNameAsync('TodoItem');
if (todoItemViewModel) {
const newTodoItem = await todoItemViewModel.createBlankInstanceAsync();
if (newTodoItem) {
// Set some initial values
newTodoItem.stringProperty('description')?.set('Buy groceries');
addInstance(newTodoItem);
}
}
};
// Insert item at specific index
const handleInsertItem = async () => {
const todoItemViewModel = await riveFile?.viewModelByNameAsync('TodoItem');
if (todoItemViewModel) {
const newTodoItem = await todoItemViewModel.createBlankInstanceAsync();
if (newTodoItem) {
addInstanceAt(newTodoItem, 0); // Insert at beginning
}
}
};
// Remove first item by instance
const handleRemoveFirst = () => {
const firstInstance = getInstanceAt(0);
if (firstInstance) {
removeInstance(firstInstance);
}
};
// Remove item by index
const handleRemoveAt = () => {
if (length > 0) {
removeInstanceAt(0);
}
};
// Swap two items
const handleSwap = () => {
if (length >= 2) {
swap(0, 1);
}
};
console.log(`List has ${length} items`);
return (
);
```
List data binding is not supported on the legacy runtime.
### Artboards
Artboard properties allows you to swap out entire components at runtime. This is useful for creating modular components that can be reused across different designs or applications, for example:
* Creating a skinning system that supports a large number of variations, such as a character creator where you can swap out different body parts, clothing, and accessories.
* Creating a complex scene that is a composition of various artboards loaded from various different Rive files (drawn to a single canvas/texture/widget).
* Reducing the size (complexity) of a single Rive file by breaking it up into smaller components that can be loaded on demand and swapped in and out as needed.
See the [React Native data binding artboards example](https://github.com/rive-app/rive-nitro-react-native/blob/main/example/src/demos/DataBindingArtboardsExample.tsx).
Artboard properties work with the `BindableArtboard` class. Use `getBindableArtboard` on a `RiveFile` to create a bindable reference, then set it on the artboard property.
```typescript theme={null}
// Get artboard property from view model instance
const artboardProp = instance.artboardProperty('CharacterArtboard');
// Create a bindable artboard and set it
const bindableArtboard = riveFile.getBindableArtboard('Character 1');
artboardProp?.set(bindableArtboard);
```
Artboard data binding is not supported on the legacy runtime.
### 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.
You can access enum properties using the `useRiveEnum` hook. The hook returns the current value and a setter function.
```tsx theme={null}
import {
useRiveFile,
useViewModelInstance,
useRiveEnum,
RiveView
} from '@rive-app/react-native';
const { riveFile } = useRiveFile(require('./my_file.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: category, setValue: setCategory, error } = useRiveEnum(
'category',
instance
);
// Set enum value
setCategory('option_name');
return (
);
```
Retrieving the list of all enums in the file, or accessing all possible values on an Enum, is not yet available.
Retrieving the list of enums is not supported on the legacy API.
# Examples
See the [example app](https://github.com/rive-app/rive-nitro-react-native/tree/main/example) for data binding demos with the new runtime.
See the [Data Binding view](https://github.com/rive-app/rive-react-native/blob/main/example/app/\(examples\)/DataBinding.tsx) in the Example app for a demo.
# Error Handling
Source: https://rive.app/docs/runtimes/react-native/error-handling
Handling errors in Rive React Native
## Error Handling
This page only applies to the [new
runtime](https://github.com/rive-app/rive-nitro-react-native) - not the legacy
runtime.
Wrap Rive operations in try/catch blocks to handle errors. For example, when loading a file:
```js theme={null}
try {
const riveFile = await RiveFileFactory.fromURL(
"https://cdn.rive.app/animations/vehicles.riv"
);
// Use the riveFile...
} catch (error) {
// Handle errors that occur during Rive file loading
console.error("Error loading Rive file:", error);
}
```
### View-Based Errors
Use the `onError` callback prop to handle errors during view configuration or runtime operations:
```js theme={null}
{
// error.type contains the error type enum value
// error.message contains a descriptive error message
console.error(`Rive Error [${error.type}]: ${error.message}`);
}}
/>
```
#### Error Types
The following error types may occur during view operations:
| Error Type | Value | Description |
| ---------------------------------------------- | ----- | ----------------------------------------------------- |
| `RiveErrorType.Unknown` | 0 | An unknown error occurred |
| `RiveErrorType.FileNotFound` | 1 | The specified Rive file could not be found |
| `RiveErrorType.MalformedFile` | 2 | The Rive file is malformed or corrupted |
| `RiveErrorType.IncorrectArtboardName` | 3 | The specified artboard name does not exist |
| `RiveErrorType.IncorrectStateMachineName` | 4 | The specified state machine name does not exist |
| `RiveErrorType.ViewModelInstanceNotFound` | 6 | The specified view model instance was not found |
| `RiveErrorType.IncorrectStateMachineInputName` | 8 | The specified state machine input name does not exist |
Use these error types to provide specific error handling:
```js theme={null}
import { RiveView, RiveErrorType } from "@rive-app/react-native";
{
switch (error.type) {
case RiveErrorType.IncorrectArtboardName:
console.error("Artboard not found:", error.message);
// Handle missing artboard (e.g., use default artboard)
break;
case RiveErrorType.IncorrectStateMachineName:
console.error("State machine not found:", error.message);
// Handle missing state machine
break;
case RiveErrorType.MalformedFile:
console.error("Corrupted file:", error.message);
// Handle corrupted file (e.g., show error UI)
break;
default:
console.error("Rive error:", error.message);
}
}}
style={{ width: "100%", height: 400 }}
/>;
```
If no `onError` handler is provided, errors will be logged to the console by
default.
### Android Runtime Initialization
On Android, the Rive native library is automatically initialized at app startup. In rare cases (ABI mismatch, missing native libraries, etc.), this initialization can fail. Use `RiveRuntime.getStatus()` to check whether initialization succeeded:
```ts theme={null}
import { RiveRuntime } from '@rive-app/react-native';
const { isInitialized, error } = RiveRuntime.getStatus();
if (!isInitialized) {
console.error('Rive failed to initialize:', error);
}
```
On iOS, the runtime requires no explicit initialization — `getStatus()` will always return `{ isInitialized: true }`.
**Advanced:** If you need full control over when initialization happens, you can disable automatic initialization by adding `Rive_RiveRuntimeAndroidSkipSetup=true` to `android/gradle.properties` and calling `RiveRuntime.initialize()` yourself. This is not recommended for most apps — only use this if you have a specific reason to defer initialization.
# Fonts
Source: https://rive.app/docs/runtimes/react-native/fonts
Loading and replacing fonts dynamically at runtime.
## Swapping Font Assets at Runtime
Fonts can be loaded dynamically at runtime. This allows you to localize your Rive content without increasing the file size of the exported .riv file.
Swapping a font asset replaces all instances of the font.
For more information, see [Loading Assets](/docs/runtimes/react-native/loading-assets).
## Fallback Fonts
When rendering text, not all glyphs (characters) may be available in the active font. This commonly occurs when:
* Using custom fonts that don’t support all languages or Unicode ranges
* The embedded font is a subset of the font
* User-generated or dynamic text contains unexpected characters
A fallback font is used automatically when the primary font cannot render a specific glyph. These are typically system fonts, which generally provide broad Unicode coverage.
On iOS and Android, font sizes specified for fallback fonts are ignored. Instead, the platform selects system fonts that best match the styling and animation of the text run at runtime.
See the complete fallback font example used in the React Native sample app.
## Loading fallback fonts
Fallback fonts can come from:
* a bundled native asset
* a remote URL
* a system font by name
```ts theme={null}
import { RiveFonts } from '@rive-app/react-native';
// Bundled native font
const bundled = await RiveFonts.loadFont('kanit_regular.ttf');
// Remote font
const remote = await RiveFonts.loadFont({
uri: 'https://raw.githubusercontent.com/google/fonts/main/ofl/kanit/Kanit-Regular.ttf',
});
// System font by name
const system = await RiveFonts.loadFont({ name: 'Thonburi' });
```
## Setting fallback fonts
Call RiveFonts.setFallbackFonts() before mounting the Rive view.
```ts theme={null}
await RiveFonts.setFallbackFonts({
default: [
bundled,
remote,
RiveFonts.systemFallback(),
],
});
```
## Clearing fallback fonts
To remove previously configured fallback fonts, call:
```ts theme={null}
await RiveFonts.clearFallbackFonts();
```
This is useful if your app needs to fully reset font configuration before recreating a RiveView.
# Layout
Source: https://rive.app/docs/runtimes/react-native/layouts
Control how graphics are laid out within the canvas.
## The Fit Mode
A Rive graphic authored in the editor will not necessarily match the size of the container it is rendered into at runtime. We need to determine the behavior for this scenario, as no one size fits all.
The solution is choosing the fit mode. This is specified on the container and controls how Rive is scaled.
* `Layout`: Use the Rive layout engine to apply responsive layout to the artboard, matching the container dimensions. For this to work, the artboard must be designed with layouts in mind. See [Responsive Layouts](#responsive-layouts) for more information on how to use this option.
* `Contain`: **(Default)** Preserve aspect ratio and scale the artboard so that its larger dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will leave space on the shorter dimension's axis.
* `ScaleDown`: Preserve aspect ratio and behave like `Contain` when the artboard is larger than the container. Otherwise, use the artboard's original dimensions.
* `Cover`: Preserve aspect ratio and scale the artboard so that its smaller dimension matches the corresponding dimension of the container.
If aspect ratios are not identical, this will clip the artboard on the larger dimension's axis.
* `FitWidth`: Preserve aspect ratio and scale the artboard width to match the container's width.
If the aspect ratios between the artboard and container do not match, this will result in either vertical clipping or space in the vertical axis.
* `FitHeight`: Preserve aspect ratio and scale the artboard height to match the container's height.
If the aspect ratios between the artboard and container do not match, this will result in either horizontal clipping or space in the horizontal axis.
* `Fill`: Do not preserve aspect ratio and stretch to the container's dimensions.
* `None`: Do not scale. Use the artboard's original dimensions.
For either dimension, if the artboard's dimension is larger, it will be clipped. If it is smaller, it will leave space.
### Alignment
In all options other than `Layout` and `Fill`, there is the possibility that the Rive graphic is clipped or leaves space within its container. Alignment determines how content aligns within the container. The following options are available.
* `TopLeft`
* `TopCenter`
* `TopRight`
* `CenterLeft`
* `Center` **(Default)**
* `CenterRight`
* `BottomLeft`
* `BottomCenter`
* `BottomRight`
### Bounds
This runtime exposes the bounding dimensions for the area in which the Rive content will render by providing the minimum and maximum x and y coordinates. These coordinates are relative to the container and all must be provided. These will override alignment settings.
* `minX`
* `minY`
* `maxX`
* `maxY`
### Applying the Fit Mode
Set layout attributes for `Fit` and `Alignment` on the `RiveView` component directly. See [Fit](#the-fit-mode) and [Alignment](#alignment) for all enum options.
```ts theme={null}
import {
Alignment,
Fit,
RiveView,
useRiveFile,
} from '@rive-app/react-native';
export default function LayoutExample() {
const { riveFile } = useRiveFile(
require('path/to/file.riv')
);
return (
{riveFile ? (
) : null}
);
}
```
Set layout attributes for `Fit` and `Alignment` on the `Rive` component directly. See [Fit](#the-fit-mode) and [Alignment](#alignment) for all enum options.
```javascript theme={null}
import Rive, { Alignment, Fit } from 'rive-react-native';
export default function Simple() {
return (
);
};
```
## Responsive Layouts
Rive’s layout feature lets you design resizable artboards with built-in responsive behavior, configured from the editor. Ensure the fit mode is set to **Layout** at runtime and the artboard will resize to fill its container according to the constraints defined in the editor.
Optionally you may provide a **layout scale factor** to multiply the scale of the content. This allows fine tuning the visual size within your container. This property only applies when setting the **Fit** mode to **Layout**.
For more Editor information and how to configure your graphic, see [Layouts Overview](/docs/editor/layouts/layouts-overview).
**Steps**
1. Set `fit` to `Fit.Layout` - this will automatically scale and resize the artboard to match the view size.
2. Optionally set the `layoutScaleFactor` for manual control of the artboard size (scale factor). If not set, the graphic uses the DPI of the device for scaling.
```javascript theme={null}
export default function ResponsiveLayoutsExample() {
const { riveFile, isLoading, error } = useRiveFile(
require('path/to/file.riv')
);
const [scaleFactor, setScaleFactor] = useState(4.0);
const riveRef = useRef(null);
const { width, height } = useWindowDimensions();
useEffect(() => {
riveRef.current?.playIfNeeded();
}, [width, height]);
const increaseScale = () => {
setScaleFactor((prev) => prev + 0.5);
riveRef.current?.playIfNeeded();
};
const decreaseScale = () => {
setScaleFactor((prev) => Math.max(0.5, prev - 0.5));
riveRef.current?.playIfNeeded();
};
return (
{isLoading ? (
) : error ? (
{error?.message}
) : riveFile ? (
(riveRef.current = ref) }}
file={riveFile}
fit={Fit.Layout}
layoutScaleFactor={scaleFactor}
style={styles.rive}
autoPlay={true}
/>
) : null}
Layout Scale Factor{scaleFactor.toFixed(1)}x
);
}
```
The call to `playIfNeeded()` ensures that the graphic is visually updated after the change. In the future this will be handled automatically.
**Examples**
* [Layout React Native Example](https://github.com/rive-app/rive-react-native/blob/main/example/app/\(examples\)/ResponsiveLayout.tsx)
**Steps**
1. Set `fit` to `Fit.Layout` - this will automatically scale and resize the artboard to match the canvas size.
2. Optionally set `layoutScaleFactor` in the `Layout` object for manual control of the artboard's scale factor.
3. The React Native runtime automatically handles window resizing and device pixel ratio changes.
```javascript theme={null}
import Rive, { Fit } from 'rive-react-native';
const resourceName = 'layout_test';
export default function ResponsiveLayout() {
return (
);
}
```
# Loading Assets
Source: https://rive.app/docs/runtimes/react-native/loading-assets
Loading and replacing assets dynamically at runtime
If you want to dynamically replace images, use image data binding.
Some Rive files may contain assets that can be embedded within the actual file binary, such as font, image, or audio files. The Rive runtimes may then load these assets when the Rive file is loaded. While this makes for easy usage of the Rive files/runtimes, there may be opportunities to load these assets in or even replace them at runtime instead of embedding them in the file binary.
There are several benefits to this approach:
* Keep the `.riv` files tiny without potential bloat of larger assets
* Dynamically load an asset for any reason, such as loading an image with a smaller resolution if the `.riv` is running on a mobile device vs. an image of a larger resolution for desktop devices
* Preload assets to have available immediately when displaying your `.riv`
* Use assets already bundled with your application, such as font files
* Sharing the same asset between multiple `.riv`s
## Methods for Loading Assets
There are currently three different ways to load assets for your Rive files.
In the Rive editor select the desired asset from the **Assets** tab, and in the inspector choose the desired export option:
### Embedded Assets
In the Rive editor, static assets can be included in the `.riv` file, by choosing the *"Embedded"* export type. As stated in the beginning of this page, when the Rive file gets loaded, the runtime will implicitly attempt to load in the assets embedded in the `.riv` as well, and you don't need to concern yourself with loading any assets manually.
**Caveat:** Embedded assets may bulk up the file size, especially when it comes to fonts when using Rive Text ([Text Overview](/docs/editor/text/text-overview)).
**Embedded is the default option.**
### Loading via Rive's CDN
In the Rive editor, you can mark an imported asset as a *"Hosted"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, but will be hosted on Rive's CDN. This means that at runtime when loading in the file, the runtime will see the asset is marked as "Hosted" and load the asset in from the Rive CDN, so that you don't need to concern yourself with loading anything yourself, and the file can still remain tiny.
**Caveat:** The app will make an extra call to a Rive CDN to retrieve your asset
Hosted assets are available on Voyager and Enterprise plans. [Learn more about
our plans and pricing](https://rive.app/pricing?utm_source=docs\&utm_medium=content).
### Image CDNs
Some image CDNs allow for on-the-fly image transformations, including resizing, cropping, and automatic format conversion based on the browser's and device's capabilities. These CDNs can host your Rive image assets. Note that for these CDNs, you may need to specify the accepted formats, for example, as part of the HTTP header request:
```html theme={null}
... headers: { Accept: 'image/png,image/webp,image/jpeg,*/*', } ...
```
Please see your CDN provider's documentation for additional information.
Rive supports the following image formats: **jpeg**, **png**, and **webp**
### Referenced Assets
In the Rive editor, you can mark an imported asset as a *"Referenced"* export type, which means that when you export the `.riv` file, the asset will not be embedded in the file binary, and the responsibility of loading the asset will be handled by your application at runtime.
This option enables you to dynamically load in assets via a handler API when the runtime begins loading in the `.riv` file. This option is preferable if you have a need to dynamically load in a specific asset based on any kind of app/game logic, and especially if you want to keep the .riv file size small.
All referenced assets, including the `.riv`, will be bundled as a zip file when you export your animation.
**Caveat:** You will need to provide an asset handler API when loading in Rive which should do the work of loading in an asset yourself. See [Handling Assets](#handling-assets).
SVG assets can't currently be loaded at runtime as referenced assets.
This is because SVGs are converted to Rive vector objects, which are always embedded into the .riv.
## Handling Assets
### Examples
### Using the Asset Handler API
To load out-of-band assets, provide a key-value object that maps expected assets to their sources when loading the Rive file.
The **key** is the name + unique identifier combination, as exported from the Rive editor.
```javascript theme={null}
const { riveFile, isLoading, error } = useRiveFile(
require('path/to/file.riv'),
{
referencedAssets: {
'Inter-594377': {
source: require('../../assets/fonts/Inter-594377.ttf'),
},
'referenced-image-2929282': {
source: { uri: 'https://picsum.photos/id/372/500/500' },
},
'referenced_audio-2929340': {
source: require('../../assets/audio/referenced_audio-2929340.wav'),
},
},
}
);
```
You can optionally exclude the unique identifier. For example, instead of `Inter-594377`, you can use `Inter`. However, it is recommended to use the full identifier to avoid potential conflicts. Using just the asset name allows you to avoid knowing the unique identifier and gives you more control over naming.
### Using Suspense
You can manage the asset decoding yourself, and share this resource across multiple Rive views.
We currently only support images, but work is underway for the other asset types.
```ts theme={null}
function getImagePromise(url: string): Promise {
return RiveImages.loadFromURLAsync(url);
}
```
```ts theme={null}
Loading image...
}
>
```
```ts theme={null}
function RiveContent({ imageUrl }: { imageUrl: string }) {
const imagePromise = React.useMemo(
() => getImagePromise(imageUrl),
[imageUrl]
);
const riveImage = React.use(imagePromise);
const { riveFile, isLoading, error } = useRiveFile(
require('../../assets/rive/out_of_band.riv'),
{
referencedAssets: {
'Inter-594377': {
source: require('../../assets/fonts/Inter-594377.ttf'),
},
'referenced-image-2929282': riveImage,
'referenced_audio-2929340': {
source: require('../../assets/audio/referenced_audio-2929340.wav'),
},
},
}
);
if (isLoading) {
return ;
} else if (error != null) {
return (
Error loading Rive file: {error?.message}
);
}
return (
);
}
```
See [this example](https://github.com/rive-app/rive-nitro-react-native/blob/main/example/src/exercisers/OutOfBandAssetsWithSuspense.tsx) for more information.
### Examples
* [Out of bands example](https://github.com/rive-app/rive-react-native/blob/main/example/app/\(examples\)/OutOfBandAssets.tsx)
### Using the Referenced Assets API
React Native has a different API for handling out-of-band assets compared to our other runtimes.
The `referencedAssets` prop accepts a key-value object. The `key` is the unique identifier of the asset (as exported in the Editor), which combines the asset name and its unique identifier. The `value` specifies how to load the asset:
* A source loaded directly from JavaScript.
* A URI pointing to an asset downloaded from the web.
* A bundled asset on the native platform (iOS and Android), included through Xcode and Android Studio, respectively.
You can optionally exclude the unique identifier. For example, instead of `Inter-594377`, you can use `Inter`. However, it is recommended to use the full identifier to avoid potential conflicts. Using just the asset name allows you to avoid knowing the unique identifier and gives you more control over naming.
The following code sample illustrates the three different ways an asset can be loaded:
```javascript theme={null}
{
console.log(riveError);
}}
/>
```
## Additional Resources
# Loading Rive Files
Source: https://rive.app/docs/runtimes/react-native/loading-rive-files
How to use Rive files with the Rive React Native runtime.
There are several ways to load Rive files in your React Native projects using the new runtime:
* **Option 1: Using `require()`** - Load files from your project directory (recommended for development and OTA updates)
* **Option 2: URL** - Load files from a remote URL
* **Option 3: Resource name** - Load files from native asset bundles
* **Option 4: ArrayBuffer** - Load files from binary data
All loading methods use the `useRiveFile` hook, which returns a `RiveFile` object that you pass to the `RiveView` component via the `file` prop.
### Option 1: Using `require()` (Recommended)
Loading Rive files using `require()` is recommended because it doesn't require a native rebuild when you update the Rive file. During development, files loaded with `require()` are served by the Metro development server. When you build your app, the file is automatically bundled into the app's assets. With Expo, this also enables Over The Air (OTA) updates.
```tsx theme={null}
import { View, ActivityIndicator, Text } from "react-native";
import { RiveView, useRiveFile, Fit } from "@rive-app/react-native";
export default function RiveDemo() {
const { riveFile, isLoading, error } = useRiveFile(
require("./assets/flying_car.riv")
);
if (isLoading) {
return ;
}
if (error || !riveFile) {
return Error: {error || "Failed to load file"};
}
return (
);
}
```
To make this work, ensure your `metro.config.js` supports `.riv` files.
If you're using Expo and don't already have this file, you can generate it with:
```bash theme={null}
npx expo customize metro.config.js
```
Then add:
```javascript theme={null}
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(__dirname);
// Add support for `.riv` files
config.resolver.assetExts.push("riv");
module.exports = config;
```
### Option 2: Loading from URL
You can load Rive files from a remote URL (e.g., AWS S3, Google Storage, CDN):
```tsx theme={null}
import { View, ActivityIndicator, Text } from "react-native";
import { RiveView, useRiveFile, Fit } from "@rive-app/react-native";
export default function RiveDemo() {
const { riveFile, isLoading, error } = useRiveFile(
"https://cdn.rive.app/animations/vehicles.riv"
);
if (isLoading) {
return ;
}
if (error || !riveFile) {
return Error: {error || "Failed to load file"};
}
return (
);
}
```
### Option 3: Loading from Resource Name
You can load Rive files from native asset bundles by referencing the resource name (without the `.riv` extension).
```tsx theme={null}
import { View, ActivityIndicator, Text } from "react-native";
import { RiveView, useRiveFile, Fit } from "@rive-app/react-native";
export default function RiveDemo() {
const { riveFile, isLoading, error } = useRiveFile("weather_app");
if (isLoading) {
return ;
}
if (error || !riveFile) {
return Error: {error || "Failed to load file"};
}
return (
);
}
```
#### Adding to iOS
In the `ios/` folder of your React Native project, open the `.xcodeproj` file in XCode. This will open up the native iOS project.
Create a New Group under the root of this project and give it a name (i.e., Assets). Drop your `.riv` file into this group, and when prompted by XCode, add it to the *Target* of your app. This ensures that the Rive file gets included in the bundle resources.
#### Adding to Android
Open the `android/` folder of your React Native project in Android Studio.
Under the `/app/src/main/res/` directory, create a new *Android Resource Directory*, which is where you'll store Rive file assets. When prompted to select a name for the folder and resource type, select `raw` from the resource type dropdown. Drop your `.riv` file into this new folder which ensures that the Rive file gets included in the bundle resources.
Adding `weather_app.riv` to the Android project
### Option 4: Loading from ArrayBuffer
You can load Rive files from binary data using an `ArrayBuffer`:
```tsx theme={null}
import { View, ActivityIndicator, Text } from "react-native";
import { RiveView, useRiveFile, Fit } from "@rive-app/react-native";
import { useState, useEffect } from "react";
export default function RiveDemo() {
const [arrayBuffer, setArrayBuffer] = useState();
useEffect(() => {
const loadFile = async () => {
try {
const response = await fetch(
"https://cdn.rive.app/animations/vehicles.riv"
);
const buffer = await response.arrayBuffer();
setArrayBuffer(buffer);
} catch (error) {
console.error("Failed to load file:", error);
}
};
loadFile();
}, []);
const { riveFile, isLoading, error } = useRiveFile(arrayBuffer);
if (isLoading || !arrayBuffer) {
return ;
}
if (error || !riveFile) {
return Error: {error || "Failed to load file"};
}
return (
);
}
```
There are several ways to include Rive files in your React Native projects:
* Option 1: URL where a Rive file is hosted
* Option 2: Add the asset to the asset bundles of the native iOS and Android projects
* Option 3: Add the asset to the asset bundles in an Expo project using `expo-asset`
* Option 4: Source prop and require
When you render the `` component, you must supply the `url` or `resourceName` prop respectively to the options above, or your component will fail to load.
### Option 1: URL
```javascript theme={null}
```
When using the Rive React Native runtime to load in a Rive file, one option is to reference the URL where the Rive file may be hosted (i.e AWS S3 bucket, Google Storage, etc.). This can be done via the `url` parameter when instantiating the `` component.
### Option 2: Asset Bundle
```javascript theme={null}
```
Another alternative to loading in a Rive file for the `` component is to reference the name of the resource/asset in the respective `ios/` and `android/` projects.
#### Adding to iOS
In the `ios/` folder of your React Native project, open the `.xcodeproj` file in XCode. This will open up the native iOS project.
Create a *New Group* under the root of this project and name it whatever asset folder name you'd like to give it (i.e., *Assets*). Drop your `.riv` file into this group, and when prompted by XCode, add it to the *Target* of your app. This ensures that the Rive file gets included in the bundle resources.
#### Adding to Android
In the `android/` folder of your React Native project, open the whole folder in Android Studio. This will open up the Android project.
Under the `/app/src/main/res/` directory, create a new *Android Resource Directory*, which is where you'll store Rive file assets, and when prompted to select a name for the folder and resource type, select `raw` from the resource type dropdown. Drop your `.riv` file into this new folder; this ensures that the Rive file gets included in the bundle resources.
Adding `weather_app.riv` to the Android project
Once the Rive files are added to the asset/resource bundles of the iOS and Android projects in the React Native app, you should be free to start referencing the name of the file (without the `.riv` extension) when creating the `` component, using that `resourceName` prop.
### Option 3: Using expo-asset with Expo CNG
```javascript theme={null}
```
If you're using Expo SDK 53 or later and want to take advantage of [Expo CNG (Continuous Native Generation)](https://docs.expo.dev/workflow/continuous-native-generation/), you can use the [expo-asset plugin](https://docs.expo.dev/versions/latest/sdk/asset/) to bundle your `.riv` files into your native builds.
In your `app.json` or `app.config.js`, add the `expo-asset` plugin and specify your `.riv` files or asset directories:
```json theme={null}
{
"expo": {
"plugins": [
[
"expo-asset",
{
"assets": ["path/to/file.riv", "path/to/directory"]
}
]
]
}
}
```
To enable support for Rive files in Metro, update your `metro.config.js`.
If you don't already have this file, generate it with:
```bash theme={null}
npx expo customize metro.config.js
```
Then edit it as follows:
```javascript theme={null}
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(__dirname);
// Add support for `.riv` files
config.resolver.assetExts.push("riv");
module.exports = config;
```
Then regenerate your development build, just remember to run `npx expo prebuild` first if you're using any `expo run:*` commands.
If you're using an earlier version of Expo, you can find an alternative approach in [this Github Issue](https://github.com/rive-app/rive-react-native/issues/185).
### Option 4: Source Prop with Require
```javascript theme={null}
```
If you prefer to keep your Rive files in the same folder as your component code, you can use the `source` prop with `require()` to load the Rive file by referencing its path.
To make this work, ensure your `metro.config.js` supports `.riv` files.
If you're using Expo and don't already have this file, you can generate it with:
```bash theme={null}
npx expo customize metro.config.js
```
Then add:
```javascript theme={null}
// Add support for `.riv` files
config.resolver.assetExts.push("riv");
```
An additional advantage of this method is that during development, the file is served by the Metro development server, allowing you to update it without rebuilding your app.
When you build your app, the file is automatically bundled into the app's assets.
# Migration Guide
Source: https://rive.app/docs/runtimes/react-native/migration-guide
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.
## Upgrading to the `v0.5` beta (new native runtime)
`v0.5` rebuilds `@rive-app/react-native` on top of Rive's new native runtimes for Android and iOS. It is currently a **beta** release.
The public React API is largely unchanged, but the deprecated synchronous APIs behave differently on the new runtime. The safest path is to get your app running cleanly on the latest `v0.4` — using the non-deprecated async APIs — *before* switching to the beta.
### Recommended path
```bash theme={null}
npm install @rive-app/react-native@0.4
```
Requires `0.4.19` or later — earlier `0.4.x` releases don't include the complete async API surface.
This keeps you on the stable runtime while you migrate your API usage.
Once you're on `v0.4`, replace the deprecated synchronous ViewModel and property accessors with their async equivalents. Convert state machine inputs, text runs, and events to [data binding](/docs/runtimes/react-native/data-binding).
See [Migrating to the Async API](#migrating-to-the-async-api-v032) below for the full list of replacements.
The async APIs work the same on `v0.4.19`+ and the `v0.5` beta. Once this migration is done, moving between the two is just a dependency change — you can try the beta and roll back to `v0.4` at any time without touching your code.
Install the beta:
```bash theme={null}
npm install @rive-app/react-native@beta
```
Report any issues on [GitHub](https://github.com/rive-app/rive-nitro-react-native/issues).
### Deprecation timeline
The deprecated synchronous APIs (and the state machine input, text run, and event methods) are being phased out as the runtime moves to the new native foundation:
| Version | Status of deprecated APIs |
| ------- | ----------------------------------------------------------------------------- |
| `v0.4` | Async replacements are available. Deprecated APIs still work with no warning. |
| `v0.5` | Calling a deprecated API logs a runtime warning. |
| `v0.6` | Deprecated APIs are removed. |
***
## Migrating to `v0.4.18`+
This release introduces async view model instance creation and deprecates the synchronous creation path.
### `useViewModelInstance` Gains `async: true` and `isLoading`
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.
```tsx theme={null}
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 theme={null}
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.
### `useViewModelInstance` Returns `{ instance, error }`
The hook now returns a discriminated union instead of `ViewModelInstance | null`:
* `{ instance: undefined, error: null }` — loading (source not ready)
* `{ instance: ViewModelInstance, error: null }` — success
* `{ instance: null, error: null }` — resolved, no ViewModel found
* `{ instance: null, error: Error }` — lookup failed
```tsx theme={null}
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance, error } = useViewModelInstance(riveFile);
if (error) return {error.message};
if (!instance) return ;
return ;
```
```tsx theme={null}
const { riveFile } = useRiveFile(require('./animation.riv'));
const instance = useViewModelInstance(riveFile);
if (!instance) return ;
return ;
```
### `useRiveFile` Error Is Now `Error`
The `error` field is now an `Error` object instead of a `string`, and `riveFile` is `undefined` while loading (was `null`). `isLoading` is kept for convenience.
```tsx theme={null}
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
if (error) return {error.message};
```
```tsx theme={null}
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
if (error) return {error};
```
### Property Hooks Start `undefined`
`useRiveNumber`, `useRiveString`, `useRiveBoolean`, `useRiveColor`, and `useRiveEnum` no longer read `property.value` synchronously on mount. The value starts as `undefined` and arrives via the property listener.
```tsx theme={null}
const { value: health } = useRiveNumber('health', instance);
// health is undefined on the first render — guard before using it
{health !== undefined ? health.toFixed(2) : '...'}
// guard in updater functions
setHealth((prev) => (prev ?? 0) + 1);
```
### Quick Reference
| Previous | Replacement |
| ------------------------------------------------ | --------------------------------------------------------- |
| `const instance = useViewModelInstance(file)` | `const { instance, error } = useViewModelInstance(file)` |
| `{error}` (in JSX, from `useRiveFile`) | `{error?.message}` |
| `riveFile === null` (loading check) | `riveFile === undefined` or use `isLoading` |
| `health` available synchronously on first render | Guard for `undefined`: `health !== undefined ? ... : ...` |
***
## Migrating to the Async API (`v0.3.2`+)
This release introduces an **async-first API** to prepare for the new experimental Rive runtime. Synchronous methods that block the JS thread are deprecated and replaced with async equivalents. State machine input and text run methods are deprecated in favor of [data binding](/docs/runtimes/data-binding) and will be removed entirely in the experimental runtime (and therefore in upcoming `@rive-app/react-native` versions).
### What's Changed
* **Async methods** replace all synchronous ViewModel and property accessors
* **Name-based access** replaces count/index-based ViewModel and artboard lookups
* **`getValueAsync()` / `set()`** replace `property.value` for reading and writing properties
* **State machine inputs, text runs, and events** are deprecated and will be removed in the experimental runtime — use [data binding](/docs/runtimes/data-binding) instead
### Migration Steps
#### 1. ViewModel Access
```tsx theme={null}
const names = await file.getViewModelNamesAsync();
const vm = await file.viewModelByNameAsync('Person');
const defaultVM = await file.defaultArtboardViewModelAsync();
```
```tsx theme={null}
const count = file.viewModelCount;
const vm = file.viewModelByName('Person');
const defaultVM = file.defaultArtboardViewModel();
```
#### 2. Instance Creation
```tsx theme={null}
const instance = await vm.createDefaultInstanceAsync();
const named = await vm.createInstanceByNameAsync('player1');
const blank = await vm.createBlankInstanceAsync();
```
```tsx theme={null}
const instance = vm.createDefaultInstance();
const named = vm.createInstanceByName('player1');
const blank = vm.createInstance();
```
The `useViewModelInstance` hook handles ViewModel resolution and instance
creation for you — in most cases you don't need to call these methods
directly.
#### 3. Property Value Access
```tsx theme={null}
const num = await prop.getValueAsync();
prop.set(42);
```
```tsx theme={null}
const num = prop.value;
prop.value = 42;
```
#### 4. Nested ViewModelInstance Access
```tsx theme={null}
const nested = await instance.viewModelAsync('Header');
```
```tsx theme={null}
const nested = instance.viewModel('Header');
```
#### 5. List Property Access
```tsx theme={null}
const len = await listProp.getLengthAsync();
const item = await listProp.getInstanceAtAsync(0);
```
```tsx theme={null}
const len = listProp.length;
const item = listProp.getInstanceAt(0);
```
#### 6. Artboard Access
```tsx theme={null}
const count = await file.getArtboardCountAsync();
const names = await file.getArtboardNamesAsync();
```
```tsx theme={null}
const count = file.artboardCount;
const names = file.artboardNames;
```
#### 7. Async Setup Pattern
Synchronous `useMemo` chains for ViewModel setup should be replaced with `useState` + `useEffect`, or simplified with the `useViewModelInstance` hook.
```tsx theme={null}
const { riveFile } = useRiveFile(require('./animation.riv'));
const { instance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: health, setValue: setHealth } = useRiveNumber(
'health',
instance
);
if (!instance) return ;
return ;
```
```tsx theme={null}
const { riveFile } = useRiveFile(require('./animation.riv'));
const [instance, setInstance] = useState();
useEffect(() => {
if (!riveFile) return;
let cancelled = false;
(async () => {
const vm = await riveFile.defaultArtboardViewModelAsync();
const vmi = await vm?.createDefaultInstanceAsync();
if (!cancelled) setInstance(vmi ?? undefined);
})();
return () => { cancelled = true; };
}, [riveFile]);
```
```tsx theme={null}
function Setup({ file }) {
const vm = useMemo(() => file.defaultArtboardViewModel(), [file]);
const instance = useMemo(
() => vm?.createDefaultInstance(),
[vm]
);
if (!instance) return null;
return ;
}
```
### Quick Reference
| Deprecated | Replacement |
| -------------------------------------- | ---------------------------------------------------------------- |
| `file.viewModelByName(name)` | `await file.viewModelByNameAsync(name)` |
| `file.defaultArtboardViewModel()` | `await file.defaultArtboardViewModelAsync()` |
| `file.viewModelCount` | `(await file.getViewModelNamesAsync()).length` |
| `file.viewModelByIndex(i)` | `await file.viewModelByNameAsync(name)` |
| `file.artboardCount` / `artboardNames` | `await file.getArtboardCountAsync()` / `getArtboardNamesAsync()` |
| `vm.createDefaultInstance()` | `await vm.createDefaultInstanceAsync()` |
| `vm.createInstanceByName(name)` | `await vm.createInstanceByNameAsync(name)` |
| `vm.createInstance()` | `await vm.createBlankInstanceAsync()` |
| `instance.viewModel(path)` | `await instance.viewModelAsync(path)` |
| `listProp.length` | `await listProp.getLengthAsync()` |
| `listProp.getInstanceAt(i)` | `await listProp.getInstanceAtAsync(i)` |
| `listProp.addInstance(inst)` | `await listProp.addInstanceAsync(inst)` |
| `listProp.addInstanceAt(inst, i)` | `await listProp.addInstanceAtAsync(inst, i)` |
| `listProp.removeInstance(inst)` | `await listProp.removeInstanceAsync(inst)` |
| `listProp.removeInstanceAt(i)` | `await listProp.removeInstanceAtAsync(i)` |
| `listProp.swap(i, j)` | `await listProp.swapAsync(i, j)` |
| `prop.value` (read) | `await prop.getValueAsync()` |
| `prop.value = x` (write) | `prop.set(x)` |
### Platform Caveats
| Limitation | Details |
| ----------------------------------------- | ---------------------------------------------------------------------------- |
| `replaceViewModel()` | No-op on Android. Works on iOS. |
| `addListener()` on image/list properties | No-op on both platforms. Poll with `getLengthAsync()` instead. |
| `addInstanceAt()` / `swap()` return value | Always `true` on iOS. Android returns correct value. |
| `instanceName` | Empty string except for instances created via `createInstanceByNameAsync()`. |
| `defaultArtboardViewModel()` on Android | Uses a heuristic. Prefer `viewModelByNameAsync(name)` with an explicit name. |
## Migrating from `v0.1.0` to `v0.2.0`
This change updates to a new major version of Nitro to resolve a view recycling issue. See the [release](https://github.com/rive-app/rive-nitro-react-native/releases/tag/v0.2.0) for more details.
The only change required is to update the version of Nitro used in your app.
## Migrating from `rive-react-native` to `@rive-app/react-native`
The new Rive React Native runtime (`@rive-app/react-native`) is a complete rewrite built with [Nitro Modules](https://nitro.margelo.com/) for improved performance and better React Native integration.
All your Rive graphics will still look and function the same as they did
before.
### What's New
**RiveFile Ownership:** You now own the `RiveFile` object via the `useRiveFile` hook, enabling file caching, multiple instances from one file, and better resource management.
```tsx theme={null}
// Old: file managed internally
// New: you own the RiveFile
const { riveFile } = useRiveFile(require("./vehicles.riv"));
```
**Enhanced Data Binding:** Direct access to `ViewModel` and `ViewModelInstance` objects, enabling initialization hooks, multiple instances, and support for all property types (lists, images, artboards).
```tsx theme={null}
// Old: hooks take riveRef, return tuples
const [setRiveRef, riveRef] = useRive();
const [health, setHealth] = useRiveNumber(riveRef, "health");
// New: hooks take viewModelInstance, return objects
const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, { async: true });
const { value: health, setValue: setHealth } = useRiveNumber(
"health",
viewModelInstance
);
```
**Improved Error Handling:**
Error handling is improved. See the [error handling](/docs/runtimes/react-native/error-handling) documentation for more information.
### Requirements
* **React Native**: 0.78+ (0.79+ recommended)
* **Expo SDK**: 53+ (for Expo users)
* **iOS**: 15.1+
* **Android**: SDK 24+
* **Xcode**: 16.4+
* **JDK**: 17+
* **Nitro Modules**: 0.25.2+
### Migration Steps
#### 1. Installation
```bash theme={null}
npm uninstall rive-react-native
npm install @rive-app/react-native react-native-nitro-modules
```
`react-native-nitro-modules` is required as this library relies on [Nitro
Modules](https://nitro.margelo.com/).
#### 2. Update Imports
```tsx theme={null}
// Old
import Rive from "rive-react-native";
// New
import { RiveView } from "@rive-app/react-native";
```
#### 3. Loading Rive Files
```tsx theme={null}
const { riveFile, isLoading, error } = useRiveFile(require('./animation.riv'));
// Also supports: URL string, resource name, or ArrayBuffer
return ;
```
```tsx theme={null}
// Also supports: url or resourceName props
```
See [loading Rive files](/docs/runtimes/react-native/loading-rive-files) for more information.
#### 4. Component Migration
```tsx theme={null}
```
```tsx theme={null}
```
See [React Native runtime](/docs/runtimes/react-native) and [props](/docs/runtimes/react-native/props) documentation for more information.
#### 5. View Reference Migration
```tsx theme={null}
const { riveViewRef, setHybridRef } = useRive();
riveViewRef?.play();
riveViewRef?.pause();
```
```tsx theme={null}
const [setRiveRef, riveRef] = useRive();
riveRef?.play();
riveRef?.pause();
```
See [Rive ref methods](/docs/runtimes/react-native/rive-ref-methods) for more information.
#### 6. State Machine Inputs (Deprecated)
These methods are deprecated. Migrate to [data binding](#8-data-binding)
instead.
```tsx theme={null}
riveViewRef?.setNumberInputValue('level', 5);
riveViewRef?.setBooleanInputValue('isActive', true);
riveViewRef?.triggerInput('buttonPressed');
```
```tsx theme={null}
riveRef.current?.setInputState('State Machine 1', 'level', 5);
riveRef.current?.setInputState('State Machine 1', 'isActive', true);
riveRef.current?.fireState('State Machine 1', 'buttonPressed');
```
See [state machine inputs](/docs/runtimes/react-native/inputs) for more information.
#### 7. Rive Events (Deprecated)
These methods are deprecated. Use [data binding triggers](#8-data-binding)
instead.
```tsx theme={null}
useEffect(() => {
const handleEvent = (event: RiveEvent) => console.log(event);
riveViewRef?.onEventListener(handleEvent);
return () => riveViewRef?.removeEventListeners();
}, [riveViewRef]);
```
```tsx theme={null}
console.log(event)}
url="..."
/>
```
See [runtime events](/docs/runtimes/react-native/rive-events) for more information.
#### 8. Data Binding
The new runtime significantly improves data binding by giving you direct access to `ViewModelInstance` objects. This enables:
* **Initialization hooks** - Set initial property values before rendering with `onInit` callback
* **Multiple instances** - Create and manage multiple view model instances from the same file
* **Advanced property types** - Full support for lists, images, artboards, and nested view models
* **Better React integration** - Property hooks integrate seamlessly with React state and lifecycle
Main API changes:
* Property hooks take `viewModelInstance` instead of `riveRef`
* Hooks return objects instead of tuples
* Parameters are swapped: `(path, viewModelInstance)` vs `(riveRef, path)`
```tsx theme={null}
const { riveFile } = useRiveFile(require('./animation.riv'));
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);
const { trigger } = useRiveTrigger('gameOver', viewModelInstance, {
onTrigger: () => console.log('Game Over!')
});
```
```tsx theme={null}
const [setRiveRef, riveRef] = useRive();
const [health, setHealth] = useRiveNumber(riveRef, 'health');
const [name, setName] = useRiveString(riveRef, 'Player/Name');
useRiveTrigger(riveRef, 'gameOver', () => console.log('Game Over!'));
```
See [react-native/data binding](/docs/runtimes/react-native/data-binding) for more information.
#### 9. Out of Band Assets
```tsx theme={null}
const { riveFile } = useRiveFile(
require('./animation.riv'),
{
referencedAssets: {
'Inter-594377': {
source: require('./fonts/Inter-594377.ttf'),
},
'my-image': {
source: { uri: 'https://example.com/image.png' },
},
},
}
);
```
```tsx theme={null}
```
See [loading assets](/docs/runtimes/react-native/loading-assets) for more information.
#### 10. Text Run Updates (Deprecated)
Direct text run methods (`.setTextRunValue()`, `.getTextRunValue()`) are
**deprecated**. Migrate to [data binding](#8-data-binding) strings instead.
```tsx theme={null}
const { value: playerName, setValue: setPlayerName } = useRiveString('playerName', viewModelInstance);
setPlayerName('John Doe');
```
```tsx theme={null}
// The new runtime still supports text run methods like the old runtime
riveViewRef?.setTextRunValue('playerName', 'John Doe');
const name = riveViewRef?.getTextRunValue('playerName');
```
```tsx theme={null}
riveRef.current?.setTextRunValue('playerName', 'John Doe');
```
See [text runs](/docs/runtimes/react-native/text) for more information.
#### 11. Callbacks
```tsx theme={null}
console.error('Rive error:', error)}
/>
// For state changes, use data binding listeners. For example, a trigger:
const { trigger } = useRiveTrigger('onStateChange', viewModelInstance, {
onTrigger: () => console.log('State changed')
});
```
```tsx theme={null}
console.log('Playing:', name)}
onPause={(name, isSM) => console.log('Paused:', name)}
onStateChanged={(sm, state) => console.log('State changed:', state)}
onError={(error) => console.error('Error:', error)}
/>
```
See [data binding](/docs/runtimes/react-native/data-binding) for more information.
### Getting Help
If you encounter issues:
1. Check the [new runtime documentation](/docs/runtimes/react-native)
2. Review the [Data Binding guide](/docs/runtimes/react-native/data-binding)
3. See the [example app](https://github.com/rive-app/rive-nitro-react-native/tree/main/example)
4. Visit the [Rive community forums](https://community.rive.app)
5. Report issues on [GitHub](https://github.com/rive-app/rive-nitro-react-native/issues)
# Native SDK Version Customization
Source: https://rive.app/docs/runtimes/react-native/native-version-customization
How to override the underlying iOS or Android Rive Native SDK version used by Rive React Native
**⚠️ Advanced Configuration**
This section is for advanced users who need to use specific versions of the Rive native SDKs. In most cases, you should use the default versions that come with the library. Only customize these versions if you have a specific requirement and understand the potential compatibility implications.
**Important:** If you customize the native SDK versions and later update Rive React Native to a newer version, you should revisit your custom version settings. The custom versions you specified may not be compatible with the updated Rive React Native version. Always check the default versions in the new release and test thoroughly.
### Default Behavior
By default, Rive React Native uses the native SDK versions specified in `package.json`:
```json theme={null}
"runtimeVersions": {
"ios": "6.12.0",
"android": "10.4.5"
}
```
These versions are tested and known to work well with this version of Rive React Native.
### Customizing Versions
You can override these default versions using platform-specific configuration files.
See the available native Rive [Android](https://github.com/rive-app/rive-android/releases) and [iOS](https://github.com/rive-app/rive-ios/releases) versions.
#### iOS (Vanilla React Native)
Create or edit `ios/Podfile.properties.json`:
```json theme={null}
{
"RiveRuntimeIOSVersion": "6.13.0"
}
```
Then run:
```bash theme={null}
cd ios && pod install
```
#### Android (Vanilla React Native)
Add to `android/gradle.properties`:
```properties theme={null}
Rive_RiveRuntimeAndroidVersion=10.5.0
```
#### Expo Projects
For Expo projects, use config plugins in your `app.config.ts`:
```typescript theme={null}
import { ExpoConfig, ConfigContext } from "expo/config";
import { withPodfileProperties } from "@expo/config-plugins";
import { withGradleProperties } from "@expo/config-plugins";
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
plugins: [
[
withPodfileProperties,
{
RiveRuntimeIOSVersion: "6.13.0",
},
],
[
withGradleProperties,
{
Rive_RiveRuntimeAndroidVersion: "10.5.0",
},
],
],
});
```
### Version Resolution Priority
The library resolves versions in the following order:
**iOS:**
1. `ios/Podfile.properties.json` → `RiveRuntimeIOSVersion`
2. `package.json` → `runtimeVersions.ios` (default)
**Android:**
1. `android/gradle.properties` → `Rive_RiveRuntimeAndroidVersion`
2. `package.json` → `runtimeVersions.android` (default)
# Playing Audio
Source: https://rive.app/docs/runtimes/react-native/playing-audio
Playing Rive audio events
To learn more on how to add audio to your Rive file, see [Audio Events](/docs/editor/events/audio-events).
## Embedded Assets
Embedded assets require no additional work to play audio.
## Referenced Assets
Referenced assets require a little bit more work to play audio. Audio will still automatically play, but the audio file(s) must be loaded when a Rive runtime attempts to play audio.
For more information, see [Loading Assets](/docs/runtimes/react-native/loading-assets).
# Props
Source: https://rive.app/docs/runtimes/react-native/props
Rive Component Props
The following are props you can set on the `RiveView` component:
The **riv** file to display, loaded via `useRiveFile` or `RiveFileFactory`.
The view reference setter, obtained from `useRive`.
Automatically start playing the state machine.
How the Rive graphic should fit within its container.
How the Rive graphic should be aligned within its container.
Ignored when using `Fit.Layout`.
The scale factor to apply to the Rive graphic when using `Fit.Layout`. The default value of `-1` uses the device's DPI.
This property has no effect for any other `Fit` type.
The name of the artboard to display.
*If not set, the default artboard will be used, as configured in the Editor.*
The name of the state machine to play.
*If not set, the default state machine will be used, as configured in the Editor.*
The view model instance to bind to the state machine. Can be:
* A `ViewModelInstance` object (from `useViewModelInstance`)
* `DataBindMode.Auto` (default) - automatically binds the default view model instance
* `DataBindMode.None` - no data binding
* `{ byName: string }` - bind by instance name
See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation for more details.
Custom error handling callback.
The following are props you can set on the Rive React component for the legacy runtime:
* `children` *(optional)* - Can be used to display something positioned `absolutely` on top of the rive animation view.
* `style` *(optional) -* Style of the rive animation view wrapper.
* Default: `undefined`
* Type: `StyleProp`
* `resourceName` *(optional)* - A file name that matches the rive file without `.riv` extension. You should provide either `resourceName` or `url` not both at the same time.
* Default: `undefined`
* Type: `string`
* `url` *(optional)* - A URL that provides a rive file. You should provide either `resourceName` or `url` not both at the same time.
* Default: `undefined`
* Type: `string`
* `autoplay` *(optional)* - Opening a rive animation view or specifying new `resourceName` or `url` will make it automatically play, when it is ready.
* Default: `true`
* Type: `boolean`
* `fit` *(optional)* - Specifies how animation should be displayed inside rive animation view
* Default: `Fit.Contain`
* Type: `Fit`
* `alignment` *(optional)* - Specifies how animation should be aligned inside rive animation view.
* Default: `Alignment.None`
* Type: `Alignment`
* `artboardName` *(optional)* - Specifies which animation artboard should be displayed in rive animation view.
* Default: `undefined`
* Type: `string`
* `animationName` *(optional)* - Specifies which animation should be played when `autoplay` is set to `true`.
* Default: `undefined`
* Type: `string`
* `stateMachineName` *(optional)* - Specifies which stateMachine should be played when `autoplay` is set to `true`.
* Default: `undefined`
* Type: `string`
* `testID` *(optional)* - Specifies testID which could be handy in tests.
* Default: `undefined`
* Type: `string`
* `onPlay` *(optional)* - Callback function that is called when animation or stateMachine has been started.
* Type: `(animationName: string, isStateMachine: boolean) => void`
* `onPause` *(optional)* - Callback function that is called when animation or stateMachine has been paused.
* Type: `(animationName: string, isStateMachine: boolean) => void`
* `onStop` *(optional)* - Callback function that is called when animation or stateMachine has been stopped.
* Type: `(animationName: string, isStateMachine: boolean) => void`
* `onLoopEnd` *(optional)* - Callback function that is called when animation loop has been ended. **Note:** This callback is only invoked if playing individual animations via the `animationName` prop, and does not get invoked if playing a state machine via the `stateMachineName` prop.
* Type: `(animationName: string, loopMode: LoopMode) => void`
* `onStateChanged` *(optional)* - Callback function that is called when the internal animation state has been changed. It's tightly coupled with state machines feature.
* Type: `(stateMachineName: string, stateName: string) => void`
* `onError` *(optional)* - Callback function that is called when error is thrown. Allows manual handling of thrown errors that are described by `RNRiveError`.
* Type: `(riveError: RNRiveError) => void`
* `onRiveEventReceived` *(optional)* - Callback function that is called when the render loop reports a Rive Event.
* Type: `(event: RiveGeneralEvent | RiveOpenUrlEvent) => void`
# React Native
Source: https://rive.app/docs/runtimes/react-native/react-native
React Native runtime for Rive.
Note that certain Rive features may not be supported yet for a particular runtime, or may require using the Rive Renderer.
For more details, refer to the [feature support](/docs/feature-support/) and [choosing a renderer](/docs/runtimes/choose-a-renderer/) pages.
🚀 **The new Rive React Native runtime is now available!** Built with Nitro for improved performance and better React Native integration.
**Get started:**
* [Migration guide](/docs/runtimes/react-native/migration-guide)
* [GitHub](https://github.com/rive-app/rive-nitro-react-native)
* [NPM](https://www.npmjs.com/package/@rive-app/react-native)
**Migration Timeline:**
* **Short term:** Complete the new runtime, see [Feature Support](https://github.com/rive-app/rive-nitro-react-native?tab=readme-ov-file#feature-support) and [Roadmap](https://github.com/rive-app/rive-nitro-react-native?tab=readme-ov-file#roadmap)
* **Medium term:** Address major concerns in the legacy package while supporting migration
* **Long term:** Full migration to the new package
We're actively gathering feedback to improve the new runtime. Please share your thoughts and report any issues you encounter.
## Overview
This guide documents how to get started using the Rive React Native runtime. The source for the new runtime is available in its [GitHub repository](https://github.com/rive-app/rive-nitro-react-native).
## Requirements
* **React Native**: 0.78 or later (0.79+ recommended for improved Android error messages)
* **Expo SDK**: 53 or later (for Expo users)
* **iOS**: 15.1 or later
* **Android**: SDK 24 (Android 7.0) or later
* **Xcode**: 16.4 or later
* **JDK**: 17 or later
* **Nitro Modules**: 0.25.2 or later
## Quick Start
Follow these quick start steps to get familiar with the Rive React Native runtime.
Remix/download the Rive file used in this quick start guide
View the complete quick start example
```bash theme={null}
npm install @rive-app/react-native react-native-nitro-modules
# or for Yarn
yarn add @rive-app/react-native react-native-nitro-modules
```
`react-native-nitro-modules` is required as this library relies on [Nitro Modules](https://nitro.margelo.com/).
Import the necessary components and define styles for the following steps.
```ts Imports theme={null}
import {
RiveView,
useRive,
useRiveFile,
useRiveNumber,
useRiveTrigger,
useViewModelInstance,
Fit,
} from '@rive-app/react-native';
```
```ts Styles theme={null}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
rive: {
width: '100%',
height: 400,
},
});
```
The `RiveView` component displays Rive graphics. It requires a single prop: `file`, a `RiveFile` object.
Use the `useRiveFile` hook to load a **riv** file and create a `RiveFile` object. This object can be cached and reused across multiple components.
```ts Loading a file theme={null}
export default function QuickStart() {
const { riveFile } = useRiveFile(
require('path/to/quick_start.riv')
);
return (
{riveFile && }
);
}
```
Further reading:
Available view props for `RiveView`
How to load Rive files in your app
Cache Rive files for better performance
Configure how the graphic fits within its container.
For this example, we'll set `fit` to `Layout`, which automatically resizes the artboard to match the view size. This is ideal for responsive Rive graphics built with [Layouts](/docs/editor/layouts/layouts-overview).
```ts Layout focus={1, 4, 5} theme={null}
```
Further reading:
Control how Rive graphics fit and align within their containers
Use the `useRive()` hook to access the Rive view reference for programmatic control.
```ts useRive hook focus={5, 11} theme={null}
export default function QuickStart() {
const { riveFile } = useRiveFile(
require('path/to/quick_start.riv')
);
const { riveViewRef, setHybridRef } = useRive();
return (
{riveFile && (
)}
);
}
```
Further reading:
See all the available view reference methods.
Read more on Nitro Hybrid Views.
Create the view model instance manually using the `useViewModelInstance` hook and pass it to the view.
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-9, 13, 19} theme={null}
export default function QuickStart() {
const { riveFile } = useRiveFile(
require('path/to/quick_start.riv')
);
const { riveViewRef, setHybridRef } = useRive();
const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, {
async: true,
onInit: (vmi) => vmi.numberProperty('health')!.set(20),
});
return (
{riveFile && viewModelInstance && (
)}
);
}
```
Use the view model property hooks to update and listen to property changes.
```ts Property hooks focus={11-38, 51-53} expandable theme={null}
export default function QuickStart() {
const { riveFile } = useRiveFile(
require('path/to/quick_start.riv')
);
const { riveViewRef, setHybridRef } = useRive();
const { instance: viewModelInstance, isLoading, error } = useViewModelInstance(riveFile, {
async: true,
onInit: (vmi) => vmi.numberProperty('health')!.set(20),
});
const { value: health, setValue: setHealth } = useRiveNumber(
'health',
viewModelInstance
);
console.log('health', health);
const { trigger: gameOverTrigger } = useRiveTrigger(
'gameOver',
viewModelInstance,
{ onTrigger: () => console.log('Game Over Triggered') }
);
const handleTakeDamage = () => {
setHealth((h) => (h ?? 0) - 7);
riveViewRef!.playIfNeeded();
};
const handleMaxHealth = () => {
setHealth(100);
riveViewRef!.playIfNeeded();
};
const handleGameOver = () => {
setHealth(0);
gameOverTrigger();
riveViewRef!.playIfNeeded();
};
return (
{riveFile && viewModelInstance && (
)}
);
}
```
We call `playIfNeeded` to force the state machine to play. Under some circumstances, the state machine might be settled if there is no active timeline in the graphic.
This is a temporary workaround. In the future, this will happen automatically.
Further reading:
See the runtime data binding documentation for more information.
See our [example app](https://github.com/rive-app/rive-nitro-react-native/tree/main/example) for more usage examples.
## Key Components
### `RiveView`
The component to render Rive content:
```ts theme={null}
```
See the available [props](/docs/runtimes/react-native/props) and [methods](/docs/runtimes/react-native/rive-ref-methods).
### `useRiveFile`
Hook for loading Rive files from a URL or local source:
```javascript theme={null}
const { riveFile } = useRiveFile(
'https://cdn.rive.app/animations/vehicles.riv'
);
// or
// const { riveFile } = useRiveFile(require('./assets/graphic.riv'));
```
See [loading Rive files](/docs/runtimes/react-native/loading-rive-files) and [caching Rive files](/docs/runtimes/react-native/caching-a-rive-file) for more information.
### `useRive`
Hook to access the Rive view reference for programmatic control:
```javascript theme={null}
const { riveViewRef, setHybridRef } = useRive();
```
This is a [Nitro Hybrid View](https://nitro.margelo.com/docs/hybrid-views). See the available [view reference methods](/docs/runtimes/react-native/rive-ref-methods).
### `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.
```ts theme={null}
// 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, isLoading, error } = useViewModelInstance(riveFile, { async: true, artboardName: 'MainArtboard' });
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, 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, 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, isLoading, error } = useViewModelInstance(riveFile, { async: true, required: true });
// With onInit to set initial values before the instance is exposed or bound
const { instance, isLoading, error } = useViewModelInstance(riveFile, {
async: true,
onInit: (vmi) => {
vmi.numberProperty('health')!.set(100);
},
});
```
`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`.
```ts theme={null}
return (
);
```
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 theme={null}
import { useRive, useViewModelInstance } from '@rive-app/react-native';
const { riveViewRef, setHybridRef } = useRive();
const { instance, isLoading, error } = useViewModelInstance(riveViewRef, { async: true });
```
See the [runtime data binding documentation](/docs/runtimes/react-native/data-binding) for more information.
## Troubleshooting
### Android build fails on Windows (CMake long-path error)
On Windows, the Android build can fail with `ninja: error: mkdir(CMakeFiles/rive.dir/...): No such file or directory` because paths exceed the Windows `MAX_PATH` (260-character) limit. This is a [known issue across React Native libraries that use CMake](https://docs.swmansion.com/react-native-reanimated/docs/guides/building-on-windows/).
To fix this, set the `CMAKE_VERSION` environment variable to a newer CMake version (e.g. `3.31.6`) before building:
```bash theme={null}
set CMAKE_VERSION=3.31.6
```
See the [Reanimated docs on building on Windows](https://docs.swmansion.com/react-native-reanimated/docs/guides/building-on-windows/) for full setup instructions.
## Resources
The legacy runtime is still supported, but we recommend migrating to the new runtime for better performance and features.
This guide documents how to get started using the legacy React Native runtime library. The source is available in its [GitHub repository](https://github.com/rive-app/rive-react-native). This library contains an API for React Native apps to easily integrate Rive assets.
The minimum iOS target is **14.0**
See [our documentation](/docs/runtimes/react-native/adding-rive-to-expo) to add
Rive to an Expo app.
## Getting Started
Follow the steps below for a quick start on integrating Rive into your React Native app.
```bash theme={null}
npm install rive-react-native
# or for Yarn
yarn add rive-react-native
```
`cd` inside the `ios` folder and run `pod install` (if deploying to iOS)
If you run into issues here, you may need to bump the `ios` deployment version target to at least `14.0`. You can find this version in the `Podfile` of the `ios/` folder.
This step may be optional - however, if your Android setup in the React Native project does not have Kotlin `v1.8.0+` set up, you may run into duplicate class issues when building the project. To mitigate these issues, as suggested by [Kotlin docs](https://kotlinlang.org/docs/gradle-configure-project.html#versions-alignment-of-transitive-dependencies), add the following to your dependencies in your application's `build.gradle` file to deal with version alignment:
```javascript theme={null}
dependencies {
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
...
}
```
```javascript theme={null}
import Rive from 'rive-react-native';
function App() {
return ;
}
```
## Resources
# Rive Ref Methods
Source: https://rive.app/docs/runtimes/react-native/rive-ref-methods
Once you have access to the Rive ref object (via `useRive` hook), there are a number of methods available to invoke for controlling your Rive graphic.
Starts playing the Rive graphic.
Pauses the Rive graphic.
Resets the Rive graphic to its initial state.
Low overhead function to ensure the Rive graphic is playing. Use after property value updates to ensure the graphic is updated.
Waits for the Rive view to be ready. Returns a promise that resolves when the Rive view is ready.
Binds a view model instance to the Rive view. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation for more details.
Gets the currently bound view model instance from the Rive view. Returns the bound `ViewModelInstance`, or `undefined` if none is bound.
Sets the text run value on the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Gets the text run value from the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Sets a number state machine input on the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Gets a number state machine input from the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Sets a boolean state machine input on the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Gets a boolean state machine input from the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Triggers a trigger state machine input on the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Adds an event listener to the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Removes all event listeners from the Rive view.
This method is deprecated. Use data binding instead. See the [Data Binding](/docs/runtimes/react-native/data-binding) documentation.
Once you have access to the Rive ref object, there are a number of methods available to invoke for controlling animations and state machines.
### .play()
A reference method that will play a singular animation or state machine. For an animation currently playing it is a no-op.
Type: `(animationName?: string, loop?: LoopMode, direction?: Direction, isStateMachine?: boolean) => void`
* `animationName` - Specifies which singular animation should be played. We **highly** recommend passing a value here
* Default: `""`
* `loop` - Specifies which `LoopMode` should be used for playing the animations.
* Default: `LoopMode.Auto`
* `direction` - Specifies which `Direction` should be used for playing the animations.
* Default: `Direction.Auto`
* `isStateMachine` - Specifies whether the passed in `animationName` is a state machine or just a linear animation.
* Default: `false`
**Example:**
```javascript theme={null}
import Rive, { RiveRef } from 'rive-react-native';
const resourceName = 'truck_v7'
function App() {
const riveRef = React.useRef(null);
const handlePlay = () => { riveRef.current?.play() };
return (
<>
# State Machine Playback
Source: https://rive.app/docs/runtimes/react-native/state-machines
Playing a state machine
For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/docs/editor/state-machine).
A Rive state machine is a set of animation states and the transitions between them. At runtime there is limited ability to observe or modify the state directly. This is by design, as this would limit the ability of a designer in Rive to modify the state machine without creating breaking changes. Instead, state machines are indirectly controlled through transitions conditioned on Data Binding properties.
A designer assigns a default state machine for each artboard in the Rive editor. They may create multiple state machines, each representing a different configuration of states and transitions. When rendering a Rive file and artboard, you may choose which state machine to play. If no state machine is specified, the default state machine for that artboard is used.
## Controlling Playback
State machines play by "advancing" over time. This is done once per frame by the amount of time between frames. For example, for a graphic running at 60 frames per second, the state machine would be advanced by approximately 16.67 milliseconds (1/60th of a second) each frame. This advancing evaluates keyframes, transitions, data bindings changes, and ultimately the visible artboard elements to create the illusion of motion over time.
This runtime provides a way to control whether the state machine is playing. When paused or stopped, the state machine does not advance and the last rendered frame remains visible. When playing from pause, the state machine resumes from where it left off, whereas when playing from stop, it restarts from the entry state.
In addition to the paused/stopped state, state machines may also "settle". This is an optimization where the Rive runtime detects that no further changes will occur (for example, if there are no active transitions or animations). While settled the state machine will also stop advancing. This improves performance and energy use by avoiding unnecessary calculations. State machines are unsettled by external actions that change their state, such as user input or data binding changes. You can additionally force a state machine to unsettle by calling play, though it may immediately re-settle if there is no further work to be done.
## Playing State Machines
By default, `RiveView` automatically uses the default artboard and state machine [configured in the Editor](/docs/editor/fundamentals/artboards#default-state-machine). In most cases, you only need to provide the `file` prop.
For programmatic control, you can optionally specify `artboardName` and `stateMachineName` props to use a different artboard or state machine.
```ts theme={null}
export default function PlaybackExample() {
const { riveFile } = useRiveFile(
'https://cdn.rive.app/animations/vehicles.riv'
);
return (
{riveFile ? : null}
);
}
```
#### Controlling State Machine Playback
For more control, you can manage playback and set the **artboard**/**state machine** combination:
Automatically start playing the state machine.
The name of the artboard to display.
*If not set, the default artboard will be used, as configured in the Editor.*
The name of the state machine to play.
*If not set, the default state machine will be used, as configured in the Editor.*
And manage `play`, `pause`, and `reset` on the Rive view reference.
```javascript theme={null}
import { Fit, RiveView, useRive, useRiveFile } from '@rive-app/react-native';
export default function PlaybackExample() {
const { riveViewRef, setHybridRef } = useRive();
const { riveFile } = useRiveFile(
'https://cdn.rive.app/animations/vehicles.riv'
);
const play = () => {
riveViewRef?.play();
};
const pause = () => {
riveViewRef?.pause();
};
const reset = () => {
riveViewRef?.reset();
};
return (
{riveFile ? (
) : null}
);
}
```
#### Autoplay the State Machine
To auto-play a state machine by default, simply set `autoPlay` to `true`.
```jsx theme={null}
```
#### Controlling State Machine Playback
You can manually play and pause the State Machine using the `play` and `pause` methods.
```jsx theme={null}
import Rive, { RiveRef } from 'rive-react-native'
export default function App() {
const riveRef = React.useRef(null);
const handlePlayPress = () => {
riveRef?.current?.play();
};
const handlePausePress = () => {
riveRef?.current?.pause();
};
return (
);
}
```
# Artboards
Source: https://rive.app/docs/runtimes/react/artboards
Selecting which artboard to render at runtime
For more information on creating artboards in the Rive editor, please refer to [Artboards](/docs/editor/fundamentals/artboards).
## Choosing an Artboard
When a Rive object is instantiated or when a Rive file is rendered, you can specify the artboard to use. If no artboard is given, the [default artboard](/docs/editor/fundamentals/artboards#default-state-machine), as set in the Rive editor, is used. If no default artboard is set, the first artboard is used.
Only one artboard can be rendered at a time.
```javascript theme={null}
export const Simple = () => (
);
// With `useRive` Hook:
export default function Simple() {
const { RiveComponent } = useRive({
src: 'https://cdn.rive.app/animations/vehicles.riv',
artboard: 'Truck',
autoplay: true,
});
return ;
}
```
# Best Practices
Source: https://rive.app/docs/runtimes/react/best-practices
Performance and usage considerations for Rive in React.
Design-time and runtime guidance that applies across platforms.
## Avoiding Unnecessary Rerenders
When using useRive, keep the hook and its returned `` together in a dedicated wrapper component.
Rive creates an instance when the component mounts, and that instance is tied to the underlying `