A free, functional, IMGUI-oriented GUI framework for rapid development of desktop applications written in Rust, supported by the Mozilla WebRender rendering engine.


  • Cross-platform GUI toolkit (Windows, Linux, Mac)
  • IMGUI / MVVM-like programming model
  • DOM-based stateless components - Widget drawing done using pure functions
  • CSS-like styling engine, support for many common CSS properties
  • Flexbox-based layout description
  • Built-in standard controls for common user interface elements
  • Custom widgets via function composition, easy dynamic UI composition
  • SVG rendering engine, 2D drawing helpers (lines, circles, rects, etc.)
  • OpenGL integration
  • Async I/O helpers
  • Optional integrated logging and error reporting helpers
  • Single deployment binary, minimal binary size (roughly 5MB all-incl.),
    CPU (0 - 4%) and RAM usage (~ 50MB total)
  • Fast redraw time (0.5 - 4ms), efficient state caching included
Calculator built using the Azul GUI framework Spreadsheet built with Azul GUI framework running on Windows SVG widget displaying a tiger on Linux

Hello world

extern crate azul;

use azul::{prelude::*, widgets::{label::Label, button::Button}};

struct MyDataModel {
    counter: usize,

impl Layout for MyDataModel {
    fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> {
        let label = Label::new(format!("{}", self.counter)).dom();
        let button = Button::with_label("Update counter").dom()
            .with_callback(On::MouseUp, Callback(update_counter));


fn update_counter(app_state: &mut AppState<MyDataModel>, _event: WindowEvent<MyDataModel>) -> UpdateScreen {
    app_state.data.modify(|state| state.counter += 1);

fn main() {
    let app = App::new(MyDataModel { counter: 0 }, AppConfig::default());
    app.run(Window::new(WindowCreateOptions::default(), Css::native()).unwrap()).unwrap();

A functional GUI framework for Rust applications

Easily compose custom widgets together by appending their DOM trees together. No macros, meta-compiler or external scripting language required. You can store your own widgets in external crates and re-use them throughout your projects.

struct DataModel {
    emails: Vec<EmailInfo>,

struct EmailInfo {
    from: String,
    subject: String,
    profile_pic: ImageId,
    message: TextId,

impl Layout for DataModel {
    fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> {

fn layout_email_component(email: &EmailInfo) -> Dom<DataModel> {

Stylable with CSS

Choose whether you want a platform-native look or a custom, flashy style!

{{ TODO: Screenshots go here }}

Simple Task-based asynchronous IO

Azul provides simple helpers for asynchronous I/O, which are thread-based. Each task is a single thread, polled for completion by azul.

impl Layout for DataModel {
    fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> {
        let button = Button::with_label("Connect to database...").dom()
            .with_callback(On::MouseUp, Callback(start_connection));

        let status = Label::new(match &self.connection_status {
            Connected    => format!("You are connected!"),
            Err(e)       => format!("There was an error: {}", e),
            InProgress   => format!("Loading..."),


fn start_connection(app_state: &mut AppState<DataModel>, _event: WindowEvent<DataModel>) -> UpdateScreen {
    app_state.data.modify(|state| state.connection_status = ConnectionStatus::InProgress);
    app_state.add_task(connect_to_db_async, &[]);

fn connect_to_db_async(app_data: Arc<Mutex<DataModel>>, _: Arc<()>) {
    thread::sleep(Duration::from_secs(2)); // simulate slow load
    app_data.modify(|state| state.connection_status = ConnectionStatus::Connected);

Simple two-way data binding

Contrary to other IMGUI-like toolkits, azul provides automatic two way data binding - only minimal code changes to go from a static label to a dynamic input form.

struct DataModel {
    text_input_state: TextInputState,

impl Layout for DataModel {
    fn layout(&self, info: WindowInfo<Self>) -> Dom<Self> {
        // Create a new text input field
        // ... bind it to self.text_input - will automatically update
        .bind(info.window, &self.text_input_state, &self)
        // ... and render it in the UI
        .with_callback(On::KeyUp, Callback(print_text_field))

fn print_text_field(app_state: &mut AppState<DataModel>, _event: WindowEvent<DataModel>) -> UpdateScreen {
    println!("You've typed: {}", app_state.data.lock().unwrap().text_input_state.text);

Interested? Check out our user guide!