Skip to content

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.

  • 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) or msbuild (Windows), for Rive’s own build
  • git: the first configure bootstraps premake5 from source

On macOS, Xcode needs its Metal compiler. If the build reports a missing Metal toolchain, install it and rebuild:

Terminal window
xcodebuild -downloadComponent MetalToolchain

The 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 the repository as a submodule:

Terminal window
git submodule add https://github.com/hypernuclear/rive-qt-plugin.git third_party/rive-qt-plugin
git submodule update --init --recursive

Then 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.

Import the module and give a RiveView a source. It loads qrc:/, file:// and http(s):// URLs:

import QtQuick
import 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.

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.

Set these when you configure (-DOPTION=VALUE):

OptionDefaultWhat it does
RIVE_QT_WITH_VULKANONBuilds the Vulkan backend. Off skips the glslang dependency.
RIVE_QT_WITH_D3D12ONBuilds the Direct3D 12 backend (Windows).
RIVE_QT_WITH_AUDIOsystemsystem plays .riv audio through the OS; external decodes and mixes but leaves output to your app; none drops audio.
RIVE_QT_WITH_SCRIPTINGONBuilds Rive’s Luau scripting. Off, scripted listeners and transitions do nothing.
RIVE_QT_WITH_RIVE_TOOLSONAllows unsigned scripts and builds the Luau compiler. Turn it off for production builds that only load signed .riv files.
RIVE_QT_BUILD_EXAMPLEStop level onlyBuilds the standalone viewer.
RIVE_QT_BUILD_TESTStop level onlyBuilds the test suite.

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.

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.

  • Hosted assets load synchronously. The .riv itself 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 installed find_package package 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.