Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up


Find what you need - explore our website and developer resources
22 April 2026
Slint is a UI framework, implemented in Rust, with support for C++, WASM, and Python. Find out more at https://slint.dev.
One thing that is needed for any UI framework is the possibility to extend the framework with your own custom UI components. That may not sound particularly special - Qt and QML already offer plenty of ways to do this, which is the reason why I won’t get into that here. As a modern programming language, Rust offers additional functionality through “crates,” which are published and distributed on crates.io, the central package registry for Rust. To extend the Slint UI framework, the natural way to accomplish this is to implement a Rust crate. In my case, this was done to provide a custom chart library. Slint is a declarative UI, meaning the UI components are described in the Slint language. For the chart library, the UI and data types are implemented in the Slint language (.slint files). The rendering of the chart is implemented in native Rust code.
So, what's the deal here, you may ask? Let's get into how Slint works under the hood. The .slint files are compiled by the slint-compiler into Rust-code (or into C++ code in the case of a C++ application). The output from the slint-compiler is included into the Rust application via the slint::include_modules!() macro. So far, so good. The Slint application (which makes use of the chart library) needs to use UI components and types that are provided by the chart library / crate. Until now, it has not been possible to export components and types provided by previously slint-compiled Rust code.
This is what I have been working on, implemented in the following PR: https://github.com/slint-ui/slint/pull/9329. The changes were merged upstream and became part of the 1.14 release. They were introduced as an experimental feature that needs to be explicitly enabled (slint-build/experimental-module-builds). Since then, Slint has moved on to the 1.16 release since April 2026. Implementing this required substantial changes to the slint-compiler.
pub so the application can access them. To configure the slint-compiler, the build.rs looks like this:fn main() {
let config = slint_build::CompilerConfiguration::new().as_library("MyLibrary").rust_module("mymodule");
slint_build::compile_with_config("mylib.slint", config);
} Cargo.toml:[dependencies]
my-library = { ... } In the .slint file, using a type or component exported by my-library is done as follows:
import { Foo } from "@MyLibrary" The slint-compiler will automatically determine that the import @MyLibrary comes from the Rust my-library crate. This “magic” is actually done by the Cargo build system. When building my-library, the following environment variables are set:
DEP_MYLIBRARY_SLINT_LIBRARY_MODULE=mymodule
DEP_MYLIBRARY_SLINT_LIBRARY_NAME=MyLibrary
DEP_MYLIBRARY_SLINT_LIBRARY_PACKAGE=my-library
DEP_MYLIBRARY_SLINT_LIBRARY_SOURCE=/absolute/path/to/mylibrary.slint These are picked up by the slint-compiler, which then knows that imports from @MyLibrary come from the my-library crate. The slint-compiler still needs to parse mylibrary.slint and add the used types into the slint-compiler type loader. In the final step, when Rust code is generated, imports from @MyLibrary result in Rust use ... statements instead of generated code.
Now let’s look at Slint globals, using a simple example:
export global MyAppData {
in property <int> some-value: 42;
callback update-data();
} Conceptually, Slint global components are just singleton instances. Technically, all Slint global components are created when the application MainWindow is created and are associated with it. When the MainWindow is destroyed, all global components are destroyed as well. The slint-compiler now needs to know which external libraries are imported, so it can also create and initialize the global components defined in those libraries. A crate can also depend on another crate. For example, let's say we have crate A, B , and C, both crate A and B have a dependency to crate C. The Slint globals for crate C will be instantiated twice. Due to that the current implementation does not yet cover all edge cases related to creating and initializing Slint global components. This is something that needs to be improved, but apart from that, it works fine.
There is still quite some work to do before this feature can move out of experimental status, but so far it is promising and already good enough to start building libraries and extensions for the Slint UI framework.
About KDAB
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Learn Rust
In collaboration with our partners Ferrous Systems, KDAB provides a variety of introductory and advanced training courses for the Rust language.
Learn more