Slint
Multi-platform native GUI toolkit. Provides declarative UI markup language and efficient rendering engine. Covers wide range from embedded systems to desktop apps with commercial support available.
GitHub Overview
slint-ui/slint
Slint is an open-source declarative GUI toolkit to build native user interfaces for Rust, C++, JavaScript, or Python apps.
Topics
Star History
Framework
Slint
Overview
Slint (formerly SixtyFPS) is an open-source declarative GUI toolkit for building native user interfaces for Rust, C++, JavaScript, and Python apps. It can run on embedded systems, desktops, mobile platforms, Android, Windows, Mac, Linux, and even web browsers via WebAssembly.
Details
Slint started as SixtyFPS in Spring 2020 and was renamed to Slint after almost two years and thirteen releases, having won many users and collected 3k GitHub Stars. By 2025, it has grown into a mature framework boasting a stable 1.0 API.
At the core of Slint is the declarative Slint markup language, which represents the entire user interface and took inspiration from Qt's QML. These .slint files are compiled ahead of time, with expressions that are pure functions the compiler can optimize. The compiler parses the Slint language and compiles straight to native Rust or C++ code, achieving native performance and efficient memory layout.
Technically, multiple rendering backends and styles are configurable at compile time: The femtovg renderer uses OpenGL ES 2.0, the skia renderer uses Skia, and the software renderer uses the CPU with no additional dependencies. This flexibility allows targeting a wide range of environments from high-end desktops to resource-constrained embedded devices.
Pros and Cons
Pros
- True Cross-Platform: Support for desktop, mobile, embedded, and web
- Declarative UI Design: Intuitive and maintainable markup language
- Multi-Language Support: Support for Rust, C++, JavaScript, Python
- High Performance: Ahead-of-time compilation and native code generation
- Diverse Rendering: Choice between OpenGL, Skia, software rendering
- Embedded Specialization: Works on microcontrollers and bare-metal environments
- Rich Development Tools: Live Preview, VS Code extension, LSP server
Cons
- Learning Curve: Requires learning the proprietary Slint language
- Ecosystem: Fewer third-party libraries compared to React or Flutter
- Newness: Relatively new framework with limited track record
- Documentation: Limited Japanese resources
- Debug Complexity: Requires knowledge of both markup language and backend language
- Compilation Dependency: Constraints on dynamic UI changes
Key Links
- Slint Official Site
- Slint GitHub Repository
- Slint Documentation
- Slint Markup Language Guide
- Slint Editor
- Slint Gallery
Code Examples
Hello World Application
// hello.slint
export component MainWindow inherits Window {
Text {
text: "Hello, Slint!";
font-size: 24px;
}
}
// main.rs
use slint::ComponentHandle;
slint::slint! {
export component MainWindow inherits Window {
Text {
text: "Hello, Slint!";
font-size: 24px;
}
}
}
fn main() -> Result<(), slint::PlatformError> {
let ui = MainWindow::new()?;
ui.run()
}
Interactive Counter App
// counter.slint
export component Counter inherits Window {
property <int> counter: 0;
VerticalBox {
Text {
text: "Counter: \{counter}";
font-size: 24px;
horizontal-alignment: center;
}
HorizontalBox {
Button {
text: "+";
clicked => { counter += 1; }
}
Button {
text: "-";
clicked => { counter -= 1; }
}
Button {
text: "Reset";
clicked => { counter = 0; }
}
}
}
}
// main.rs
use slint::ComponentHandle;
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
let ui = Counter::new()?;
ui.run()
}
Complex UI Layout and Styling
// app.slint
struct Person := {
name: string,
age: int,
email: string,
}
export component PersonForm inherits Window {
property <Person> person: { name: "", age: 0, email: "" };
property <bool> is-valid: person.name != "" && person.email != "";
callback submit-form(Person);
VerticalBox {
padding: 20px;
spacing: 10px;
Text {
text: "Personal Information";
font-size: 20px;
font-weight: 700;
color: #2c3e50;
}
GridLayout {
Row {
Text { text: "Name:"; }
LineEdit {
text: person.name;
edited => { person.name = self.text; }
placeholder-text: "Enter your name";
}
}
Row {
Text { text: "Age:"; }
SpinBox {
value: person.age;
edited => { person.age = self.value; }
minimum: 0;
maximum: 150;
}
}
Row {
Text { text: "Email:"; }
LineEdit {
text: person.email;
edited => { person.email = self.text; }
placeholder-text: "[email protected]";
}
}
}
Button {
text: "Submit";
enabled: is-valid;
clicked => { submit-form(person); }
background: is-valid ? #3498db : #95a5a6;
}
if !is-valid : Text {
text: "Please fill in all required fields";
color: #e74c3c;
font-size: 12px;
}
}
}
// main.rs
use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel};
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
let ui = PersonForm::new()?;
ui.on_submit_form(|person| {
println!("Form submitted:");
println!(" Name: {}", person.name);
println!(" Age: {}", person.age);
println!(" Email: {}", person.email);
});
ui.run()
}
Lists and Data Binding
// todo.slint
struct TodoItem := {
text: string,
completed: bool,
}
export component TodoApp inherits Window {
property <[TodoItem]> todos: [
{ text: "Learn Slint", completed: false },
{ text: "Build an app", completed: false },
{ text: "Ship it!", completed: false },
];
property <string> new-todo-text;
callback add-todo();
callback toggle-todo(int);
callback remove-todo(int);
VerticalBox {
padding: 20px;
spacing: 10px;
Text {
text: "Todo List";
font-size: 24px;
horizontal-alignment: center;
}
HorizontalBox {
LineEdit {
text: new-todo-text;
placeholder-text: "What needs to be done?";
edited => { new-todo-text = self.text; }
}
Button {
text: "Add";
enabled: new-todo-text != "";
clicked => { add-todo(); }
}
}
for todo[index] in todos : Rectangle {
height: 40px;
border-width: 1px;
border-color: #ddd;
HorizontalBox {
padding: 10px;
spacing: 10px;
CheckBox {
checked: todo.completed;
toggled => { toggle-todo(index); }
}
Text {
text: todo.text;
vertical-alignment: center;
color: todo.completed ? #888 : #000;
}
Button {
text: "×";
width: 30px;
clicked => { remove-todo(index); }
}
}
}
Text {
text: "Total: \{todos.length} items";
horizontal-alignment: center;
color: #666;
}
}
}
// main.rs
use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel};
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
let ui = TodoApp::new()?;
let ui_handle = ui.as_weak();
// Add todo callback
ui.on_add_todo(move || {
let ui = ui_handle.unwrap();
let text = ui.get_new_todo_text();
if !text.is_empty() {
let mut todos = ui.get_todos();
todos.push(TodoItem {
text: text.clone(),
completed: false,
});
ui.set_todos(todos.into());
ui.set_new_todo_text(SharedString::new());
}
});
// Toggle todo callback
let ui_handle = ui.as_weak();
ui.on_toggle_todo(move |index| {
let ui = ui_handle.unwrap();
let mut todos = ui.get_todos();
if let Some(todo) = todos.get_mut(index as usize) {
todo.completed = !todo.completed;
}
ui.set_todos(todos.into());
});
// Remove todo callback
let ui_handle = ui.as_weak();
ui.on_remove_todo(move |index| {
let ui = ui_handle.unwrap();
let mut todos = ui.get_todos();
todos.remove(index as usize);
ui.set_todos(todos.into());
});
ui.run()
}
Custom Components and Animation
// components.slint
export component AnimatedButton inherits Rectangle {
property <string> text;
property <color> base-color: #3498db;
property <bool> pressed: area.pressed;
callback clicked;
width: 120px;
height: 40px;
background: pressed ? base-color.darker(20%) : base-color;
border-radius: 8px;
animate background { duration: 150ms; }
area := TouchArea {
clicked => { clicked(); }
}
Text {
text: parent.text;
color: white;
font-size: 14px;
horizontal-alignment: center;
vertical-alignment: center;
}
}
export component LoadingSpinner inherits Rectangle {
property <duration> rotation-duration: 1s;
property <bool> spinning: true;
width: 40px;
height: 40px;
spinner := Rectangle {
width: 100%;
height: 100%;
border-width: 4px;
border-color: transparent;
border-radius: 50%;
background: conic-gradient(0deg, transparent 0%, #3498db 50%, transparent 100%);
animate rotation-angle {
duration: rotation-duration;
iteration-count: spinning ? -1 : 0;
easing: linear;
}
rotation-angle: spinning ? 360deg : 0deg;
}
}
export component MainApp inherits Window {
property <bool> is-loading: false;
VerticalBox {
padding: 30px;
spacing: 20px;
Text {
text: "Custom Components Demo";
font-size: 24px;
horizontal-alignment: center;
}
HorizontalBox {
spacing: 15px;
AnimatedButton {
text: "Start";
base-color: #27ae60;
clicked => {
is-loading = true;
// Timer to stop after 3 seconds
}
}
AnimatedButton {
text: "Stop";
base-color: #e74c3c;
clicked => { is-loading = false; }
}
}
if is-loading : LoadingSpinner {
horizontal-alignment: center;
}
Text {
text: is-loading ? "Loading..." : "Ready";
horizontal-alignment: center;
color: is-loading ? #3498db : #27ae60;
}
}
}
Project Setup and Build
# Create new Rust project
cargo new slint-app
cd slint-app
# Cargo.toml dependencies
cat >> Cargo.toml << 'EOF'
[dependencies]
slint = "1.0"
[build-dependencies]
slint-build = "1.0"
EOF
# Create build.rs file (for compiling .slint files)
cat > build.rs << 'EOF'
fn main() {
slint_build::compile("ui/app.slint").unwrap();
}
EOF
# Create UI directory and files
mkdir ui
touch ui/app.slint
# Create basic Slint file
cat > ui/app.slint << 'EOF'
export component MainWindow inherits Window {
Text {
text: "Hello from Slint!";
font-size: 24px;
}
}
EOF
# Update main.rs
cat > src/main.rs << 'EOF'
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
let ui = MainWindow::new()?;
ui.run()
}
EOF
# Development run
cargo run
# Release build
cargo build --release
# Specify different rendering backends
# Software rendering
cargo run --no-default-features --features backend-winit,renderer-software
# OpenGL ES
cargo run --no-default-features --features backend-winit,renderer-femtovg
# Skia rendering
cargo run --features backend-winit,renderer-skia
# Web application build
rustup target add wasm32-unknown-unknown
cargo install trunk
# Create index.html file
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Slint Web App</title>
</head>
<body></body>
</html>
EOF
# Add wasm configuration to Cargo.toml
cat >> Cargo.toml << 'EOF'
[target.'cfg(target_arch = "wasm32")'.dependencies]
slint = { version = "1.0", features = ["backend-winit", "renderer-femtovg"] }
EOF
# Build and run web app
trunk serve --release
# Cross-platform builds
# For Windows
cargo build --target x86_64-pc-windows-gnu --release
# For Android (requires NDK)
cargo install cargo-apk
cargo apk build --release
# For embedded Linux
cargo build --target armv7-unknown-linux-gnueabihf --release