Getting started with Rive in Qt
rive-qt-plugin builds from source alongside your app: add it to your CMake
project, link one target, and RiveView is available in QML.
Requirements
Section titled “Requirements”- Qt 6.5 or later (6.12 tested)
- CMake 3.21 or later
- A C++20 compiler: Xcode’s clang, MSVC 2022, or a recent GCC
make(macOS and Linux) ormsbuild(Windows), for Rive’s own buildgit: the first configure bootstrapspremake5from source
On macOS, Xcode needs its Metal compiler. If the build reports a missing Metal toolchain, install it and rebuild:
xcodebuild -downloadComponent MetalToolchainThe Vulkan backend (on by default) compiles its shaders with glslang and
spirv-tools. Windows builds use copies that ship with the plugin. On macOS,
install them with brew install glslang spirv-tools; on Linux, from your
distribution’s packages. Or turn Vulkan off (see Build options).
Add it to your project
Section titled “Add it to your project”Add the repository as a submodule:
git submodule add https://github.com/hypernuclear/rive-qt-plugin.git third_party/rive-qt-plugingit submodule update --init --recursiveThen link it in your CMakeLists.txt:
add_subdirectory(third_party/rive-qt-plugin)target_link_libraries(my_app PRIVATE rive_qt::rive_qt)The first configure takes a minute or two while it bootstraps premake5 and
builds the Rive runtime. Builds after that are fast.
Show a .riv
Section titled “Show a .riv”Import the module and give a RiveView a source. It loads qrc:/, file://
and http(s):// URLs:
import QtQuickimport Hypernuclear.Rive
Window { width: 640 height: 480 visible: true
RiveView { anchors.fill: parent source: "qrc:/animations/hero.riv" fit: RiveView.Fit.Contain onLoadFailed: reason => console.warn("Rive:", reason) }}RiveView plays the artboard’s default state machine, or its first animation
when there’s no state machine. Mouse, touch and keyboard input go to the state
machine’s listeners. To pick a different artboard or machine, set artboard
and stateMachineName.
Drive it with data binding
Section titled “Drive it with data binding”Rive has replaced state machine inputs and events with data binding, so
that’s how your app talks to the artboard. RiveView binds the artboard’s
default view model and exposes it as viewModel:
RiveView { id: rive source: "qrc:/animations/scoreboard.riv"
onViewModelChanged: { if (!viewModel) return viewModel.number("score").value = 42 viewModel.trigger("celebrate").triggered.connect(() => console.log("celebrated")) }}
Button { text: "Celebrate" onClicked: rive.viewModel.trigger("celebrate").fire()}For plain values you can also bind declaratively through props, e.g.
text: rive.viewModel ? rive.viewModel.props.title : "". See
RiveViewModelInstance for
every property type.
Build options
Section titled “Build options”Set these when you configure (-DOPTION=VALUE):
| Option | Default | What it does |
|---|---|---|
RIVE_QT_WITH_VULKAN | ON | Builds the Vulkan backend. Off skips the glslang dependency. |
RIVE_QT_WITH_D3D12 | ON | Builds the Direct3D 12 backend (Windows). |
RIVE_QT_WITH_AUDIO | system | system plays .riv audio through the OS; external decodes and mixes but leaves output to your app; none drops audio. |
RIVE_QT_WITH_SCRIPTING | ON | Builds Rive’s Luau scripting. Off, scripted listeners and transitions do nothing. |
RIVE_QT_WITH_RIVE_TOOLS | ON | Allows unsigned scripts and builds the Luau compiler. Turn it off for production builds that only load signed .riv files. |
RIVE_QT_BUILD_EXAMPLES | top level only | Builds the standalone viewer. |
RIVE_QT_BUILD_TESTS | top level only | Builds the test suite. |
Typed names for your files
Section titled “Typed names for your files”Artboards, state machines and view model properties are looked up by name, so
a typo fails quietly at runtime. qt_add_rive_assets() reads your .riv
files at build time and generates their names as constants:
qt_add_rive_assets(my_app NAMESPACE MyApp::Rive QML_URI MyApp.Rive ASSETS animations/scoreboard.riv)C++ gets a header per file, scoreboard_assets.h, already on the target’s
include path. Its constants look like this, and a misspelled one is a compile
error:
namespace MyApp::Rive::Scoreboard::ViewModels::Scoreboard { namespace Properties { inline constexpr std::string_view score = "score"; } namespace Triggers { inline constexpr std::string_view celebrate = "celebrate"; }}With QML_URI, each file also gets a QML singleton named after it:
import MyApp.Rive
Button { text: "Celebrate" onClicked: rive.viewModel .trigger(Scoreboard.viewModels.scoreboard.triggers.celebrate) .fire()}The generated names cover artboards and their state machines and text runs,
and each view model’s instances, properties, triggers and nested view models.
Names are turned into valid identifiers (State Machine 1 becomes
State_Machine_1 in C++ and state_Machine_1 in QML); open the generated
files to see exactly what you got.
Try your files in the viewer
Section titled “Try your files in the viewer”The repository includes a viewer that opens any .riv, lets you pick the
artboard, state machine or animation, scrub timelines and edit view model
properties live. See the viewer for how to build and
use it.
Good to know
Section titled “Good to know”- Hosted assets load synchronously. The
.rivitself loads off the render thread, but images, fonts and audio the file references (rather than embeds) are fetched while it imports. Embed assets, or keep them small. - Source only. You consume the plugin with
add_subdirectory; there is no installedfind_packagepackage yet. - Sharing a view model across windows. Several
RiveViews, even in different windows, can share one.riv. Sharing a single view model instance between windows isn’t safe yet.