Iโm Marc and I am happy to announce the first alpha (v0.1) of Freya, an experimental cross-platform native GUI library for ๐ฆ Rust, built on top of ๐งฌ Dioxus and powered by the ๐ผ๏ธ Skia library.
Website: freyaui.dev/
Source Code: github.com/marc2332/freya
Book: book.freyaui.dev/
Discord: https://discord.gg/sYejxCdewG
Stable API docs: docs.rs/freya/latest/freya/
Nightly API docs: docs.freyaui.dev/freya
Freya is a GUI library that extends Dioxus by adding its own renderer based on Skia with the help of a custom layout library, its own elements namespace, attributes and events, plus a set of components and hooks, and also some developer tools like a Devtools panel or a headless components testing runner.
Read this. Just be aware that it is still in the โ ๏ธ experimentation phase, I would like this first release to be an opportunity to gather feedback, suggestions, new contributions and ideas!
Dioxus is a renderer-agnostic UI library for Rust, like React. It uses components as functions and hooks. It supports many renderers: web, backend with SSR, liveview and fullstack, desktop and mobile with webview, desktop with WGPU, or even TUI.
Learn more about Dioxus. And see the differences with Freya.
Dioxus: very easy to use, itโs blazingly fast and has a bright future.
Native renderer: Looks the same on all platforms and avoids compatibility issues
Built-in Components and hooks: From a simple Button to a VirtualScrollView or animation utilities.
Headless testing runner: Make sure your components work before making a release.
Languages: Just Rust!
Itโs still experimental: it might contain bugs, have performance issues or some APIs not being fully usable yet.
Lack of ecosystem: You can use renderer-agnostic libraries of Dioxus but it is still a small ecosystem, and non-existent for Freya.
Lack of docs: Some things are not fully documented and others could be improved.
Here there is a simple counter app with Freya (source code here):
fn app(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
render!(
rect {
height: "20%",
width: "100%",
background: "rgb(233, 196, 106)",
padding: "12",
color: "rgb(20, 33, 61)",
label {
font_size: "20",
"Number is: {count}"
}
}
rect {
height: "80%",
width: "100%",
background: "rgb(168, 218, 220)",
color: "black",
padding: "12",
onclick: move |_| count += 1,
label { "Click to increase!" }
}
)
}
โ๏ธ Built-in components (button, scroll views, switch and more)
๐ Built-in hooks library (animations, text editing and more)
๐ Built-in devtools panel (experimental โ ๏ธ)
๐งฐ Built-in headless testing runner for components
๐จ Theming support (not extensible yet โ ๏ธ)
๐ฉ๏ธ Cross-platform (Windows, Linux, MacOS)
๐ผ๏ธ SKSL Shaders support
๐๏ธ Dioxus Hot-reload integration
๐ Multi-line text editing (experimental โ ๏ธ)
๐ฆพ Basic Accessibility Support (experimental โ ๏ธ)
๐งฉCompatible with dioxus-std and other Dioxus renderer-agnostic libraries
All major desktop OS:
Windows
Linux
macOS
It could technically run on more platforms, like Mobile or Web via Wasm, feel free to contribute ๐
Freya supports Dioxusโs hot reload, which means that you can write and update the layout, styling and other static attributes of your components without having to recompile any rust code, it updates on the fly.
Freya supports headless testing of components, you can simulate from the window size to events, like mouse or keyboard, and also assert the layout or text values of your components.
Simple example:
#[tokio::test]
async fn no_state() {
fn no_state_app(cx: Scope) -> Element {
render!(
label {
"Hello"
}
)
}
let mut utils = launch_test(no_state_app);
assert_eq!(utils.root().get(0).get(0).text(), Some("Hello"));
}
A more complex example that even simulates click events:
#[tokio::test]
async fn simulate_events() {
fn stateful_app(cx: Scope) -> Element {
let enabled = use_state(cx, || false);
render!(
rect {
width: "100%",
height: "100%",
background: "red",
direction: "both",
onclick: |_| {
enabled.set(true);
},
label {
"Is enabled? {enabled}"
}
}
)
}
let mut utils = launch_test(stateful_app);
let rect = utils.root().get(0);
let label = rect.get(0);
// Inital render
utils.wait_for_update().await;
let text = label.get(0);
assert_eq!(text.text(), Some("Is enabled? false"));
utils.push_event(FreyaEvent::Mouse {
name: "click".to_string(),
cursor: (5.0, 5.0).into(),
button: Some(MouseButton::Left),
});
// New render after clicking
utils.wait_for_update().await;
let text = label.get(0);
assert_eq!(text.text(), Some("Is enabled? true"));
}
Freya integrates an experimental DevTools panel to help you navigate and inspect the elements of your app while you are developing. It is not included in release builds.
You can do:
Inspect the DOM
Inspect elementโs styles
Inspect the elementโs computed layout
For simpler examples see the Freya repository.
Freya-editor: An experimental code editor.
Canvas: A canvas for floating editors that you can drag around.
More elements, components, hooks and events.
Better performance, both for rendering and layout.
Better developer tools (Devtools panel, test runner, etc)
More and better documentation
I am aware Freya is not perfect, and it will take some time to be production-ready, but I am sure it will keep getting better.
I hope you liked this post and want to try Freya at some point, or even contribute!
Make sure to give Dioxus some love, Jonathan, Evan and the other contributors have made an amazing work with Dioxus, they have been a fundamental piece for Freya to work ๐ซ.
Also thanks to Armin for his amazing work in rust-skia and help with issues and doubts ๐ช, and Tropix126 for his work in new styling features like more font customization, better rounded corners, better shadows and a few more things ๐ฏ !
You can leave a star โญ in the repository or sponsor me on GitHub Sponsors if you want ๐. You can join the Discord server or follow me on ๐ฆ Twitter, I usually share the progress I make in Freya.
Thanks!