This Forge asset is a learning resource, not a reusable component. It demonstrates one approach to integrating Rive animations into an OutSystems Reactive Web application using a friendly login bear example.
You are not expected to consume the LoginBear block as a dependency in your own apps. The block, the script, and the wiring are tightly coupled to this specific animation file and login flow. Treat them as a worked example: read the source, understand the moving parts, and copy the bits you need into your own module to fit your own animation and use case.
LoginBear
If you happen to want exactly this bear behaving in exactly this way, you can use the block as-is, but the more common path is to fork it as a starting point.
RiveIntegration
RiveIntegrationDemo).
RiveLoginBear.js
There is intentionally no "add the block as a dependency and you're done" path here. Rive integration involves a .riv file (renamed to .bin so Service Studio can understand it) specific to your design, state machine inputs unique to your animation, and DOM elements unique to your UI, none of which generalize cleanly across apps.
.riv file (renamed to .bin so Service Studio can understand it)
.bin so Service Studio can understand it)
RiveLoginBear.create(...)
destroy()
TriggerSuccess
TriggerFail
RiveLoginBear
login-teddy.bin
Use the demo as a recipe. Here are the moving parts you'll need to recreate in your own module, in roughly this order.
Export your animation from the Rive editor and rename it to a ".bin" file. In Service Studio, add it as a Resource to your module with Deploy Action: Deploy to Target Directory. The file will be served from a URL like /YourModuleName/your-animation.bin.
.bin"
/YourModuleName/your-animation.bin
Note the names of your state machine and its inputs (booleans, numbers, triggers) — you'll need them in the JavaScript.
Copy RiveLoginBear.js from the demo into your own module's Scripts folder and adapt it. The key things to change:
RIVE_CDN
rive.min.js
RIVE_FILE_URL
.bin
STATE_MACHINE_NAME
inputs
_onRiveLoaded
isChecking
isHandsUp
numLook
trigSuccess
trigFail
_attachListeners
The two parts you should keep verbatim unless you really know what you're doing:
loadRuntime
window.define = undefined
prefers-reduced-motion
Build a Reactive block with:
<canvas>
<canvas></canvas>
OnReady
OnDestroy
Add your script to the block's Required Scripts so the platform loads it before OnReady runs.
If your animation has trigger inputs you want to fire from OutSystems logic — for example, after a server action returns success or failure — wrap them in client actions that look like:
const instance = YourClass.get($parameters.RootId); if (instance) instance.triggerSuccess();
This keeps consumers of your block in OutSystems-land.
OutSystems and AMD modules. The Rive runtime is distributed as a UMD bundle. OutSystems pages have an AMD loader (define.amd) globally available, which causes UMD bundles to register as anonymous AMD modules instead of attaching to window.rive. The result: window.rive is undefined even though the script loaded successfully. The fix is to temporarily set window.define = undefined while the Rive script loads, then restore it. The demo's loadRuntime method does this.
define.amd
window.rive
undefined
OnReady can fire more than once. Reactive Web's OnReady can re-run on the same block instance (after async data, navigation, etc.). The class handles this by destroying any previous instance attached to the root element before creating a new one. If you skip that guard, you'll leak event listeners and Rive instances on every re-render.
State machine triggers aren't queued. If you fire a trigger while the state machine is mid-transition from another state, the trigger may be silently dropped because no transition in the current state listens for it. The demo demonstrates this limitation (focus the password field, then immediately click Login — the success animation often won't play). The cleanest fix lives in the Rive file itself: add transitions from every interactive state directly to your success/fail states. A JavaScript-side workaround using EventType.StateChange is also possible.
EventType.StateChange
Reduced motion. The demo respects prefers-reduced-motion and skips starting the state machine when it's enabled. You should keep this in your own integration — animations can cause real distress for some users, and respecting the OS setting is a one-line check.
The animation doesn't appear and the console shows "Rive runtime is not available." window.rive is undefined. Check that your loadRuntime method includes the window.define = undefined workaround, and that you're calling it before trying to construct a Rive instance.
Rive
The animation loads but doesn't react to user input. The widget IDs you passed to the class don't resolve to the actual <input> elements. OutSystems sometimes places IDs on a wrapper rather than the input itself. The demo's resolveInputElement helper handles this — make sure you keep it (or its equivalent) in your version.
<input>
resolveInputElement
Triggers don't play the animation. Either the trigger name in JavaScript doesn't match the input name in the Rive file (case-sensitive), or the state machine has no transition listening for that trigger from its current state. Check both. In DevTools, you can inspect document.querySelector("#YOUR_ROOT_ID").__riveLoginBear.inputs to see what got wired up.
document.querySelector("#YOUR_ROOT_ID").__riveLoginBear.inputs
The Rive script doesn't load at all. Check the Network tab. If the request to the CDN is blocked, your environment's Content Security Policy probably doesn't allow cdn.jsdelivr.net. Either whitelist it in Service Center, or download rive.min.js and add it as a script resource in your module.
cdn.jsdelivr.net
The JavaScript class is heavily commented. Open RiveLoginBear.js in the Scripts folder of the RiveIntegration module and read it top-to-bottom, it's the most useful part of this asset.