Need help with azul?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

4.5K Stars 179 Forks Other 1.9K Commits 43 Opened issues


Desktop GUI Framework

Services available


Need anything else?

Contributors list

Azul - Desktop GUI framework

WARNING: The features advertised in this README may not work yet.

Build Status Linux / macOS Build status Windows Coverage Status LICENSE Rust Compiler Version <!-- [END badges] -->

Azul is a free, functional, reactive GUI framework for Rust and C++, built using the WebRender rendering engine and a CSS / HTML-like document object model for rapid development of beautiful, native desktop applications

Website | User guide | API documentation | Video demo | Matrix Chat


Azul is a library for creating graphical user interfaces in Rust and C. It mixes paradigms from functional, reactive and data-oriented programming with an API suitable for developing cross-platform desktop applications. The two core principles of Azul is to not render objects that aren't visible and to use composition of DOM trees over inheritance.

Azul separates the concerns of business logic / callbacks, data model and UI rendering / styling by not letting the UI / rendering logic have mutable access to the application data. In Azul, rendering the view is a pure function that maps your application data to a styled DOM. "Widgets" are just functions that render a certain state, more complex widgets use function composition.

Since recreating DOM objects is expensive (note: "expensive" = 3 milliseconds), Azul caches the DOM object and does NOT recreate it on every frame - only when callbacks request to recreate it.

The application and widget data is managed using a reference-counted boxed type (

), which can be downcasted to a concrete type if necessary. Widget-local data that needs to be retained between frames is stored on the DOM nodes themselves, similar to how the HTML
property can be used to store widget data.


Due to its relatively large size (and to provide C / C++ interop), azul is built as a dynamic library in the

package. You can download pre-built binaries from

Using pre-built-binaries

  1. Download the library from
  2. Set your linker to link against the library
    • Rust: Set
      environment variable to the path of the library
    • C / C++: Copy the
      on the release page to your project headers and the
      to your IDE project.

The API for Rust, C++ and other languages is exactly the same, since the API is auto-generated by the
script. If you want to generate language bindings for your language, you can generate them using the

To run programs on Linux, you may also need to copy the
Eventually this will be solved by upstreaming the library into repositories once all major bugs are resolved.

Building from source (

By default, you should be able to run

cargo install --version 1.0.0 azul-dll

to compile the DLL from The library will be built and installed in the

directory, which defaults to

Building from source (git)

Building the library from source requires clang as well as the prerequisites listed above.

git clone
cd azul-dll
cargo build --release

This command should produce an

file in the
folder, in order to use this, you will also need to set

If you are developing on the library, you may also need to re-generate the Rust / C API, in which case you should prefer to use the
python3 ./


Note: The widgets are custom to each programming language. All callbacks have to use

extern "C"
in order to be compatible with the library. The binary layout of all API types is described in the

See the /examples folder for example code in different languages

Hello World Application


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

struct DataModel { counter: usize, }

// Model -> View extern "C" fn render_my_view(data: &RefAny, _: LayoutInfo) -> StyledDom {

let mut result = StyledDom::default();

let data = match data.downcast_ref::<datamodel>() {
    Some(s) =&gt; s,
    None =&gt; return result,

let label = Label::new(format!("{}", data.counter)).dom();
let button = Button::with_label("Update counter")
    .onmouseup(update_counter, data.clone())



// View updates model extern "C" fn update_counter(data: &mut RefAny, event: CallbackInfo) -> UpdateScreen { let mut data = match data.downcast_mut::() { Some(s) => s, None => return UpdateScreen::DoNothing, }; data.counter += 1; UpdateScreen::RegenerateDomForCurrentWindow }

fn main() { let app = App::new(RefAny::new(DataModel { counter: 0 }), AppConfig::default());; }


#include "azul.h"
#include "azul-widgets.h"

using namespace azul; using namespace azul.widgets.button; using namespace azul.widgets.label;

struct DataModel { counter: uint32_t }

// Model -> View StyledDom render_my_view(const RefAny& data, LayoutInfo info) {

auto result = StyledDom::default();

const DataModel* data = data.downcast_ref();
if !(data) {
    return result;

auto label = Label::new(String::format("{counter}", &amp;[data.counter])).dom();
auto button = Button::with_label("Update counter")
   .onmouseup(update_counter, data.clone())

result = result

return result;


UpdateScreen update_counter(RefAny& data, CallbackInfo event) { DataModel data = data.downcast_mut().unwrap(); data.counter += 1; return UpdateScreen::RegenerateDomForCurrentWindow; }

int main() { auto app = App::new(RefAny::new(DataModel { .counter = 0 }), AppConfig::default());; }


#include "azul.h"

typedef struct { uint32_t counter; } DataModel;

void DataModel_delete(DataModel* restrict A) { } AZ_REFLECT(DataModel, DataModel_delete);

AzStyledDom render_my_view(AzRefAny* restrict data, AzLayoutInfo info) {

AzString counter_string;

DataModelRef d = DataModelRef_create(data);
if (DataModel_downcastRef(data, &amp;d)) {
    AzFmtArgVec fmt_args = AzFmtArgVec_fromConstArray({{
        .key = AzString_fromConstStr("counter"),
        .value = AzFmtValue_Uint(d.ptr-&gt;counter)
    counter_string = AzString_format(AzString_fromConstStr("{counter}"), fmt_args);
} else {
    return AzStyledDom_empty();

AzDom const html = {
    .root = AzNodeData_new(AzNodeType_Body),
    .children = AzDomVec_fromConstArray({AzDom_new(AzNodeType_Label(counter_string))}),
    .total_children = 1, // len(children)
AzCss const css = AzCss_fromString(AzString_fromConstStr("body { font-size: 50px; }"));
return AzStyledDom_new(html, css);


UpdateScreen update_counter(RefAny& data, CallbackInfo event) { DataModelRefMut d = DataModelRefMut_create(data); if !(DataModel_downcastRef(data, &d)) { return UpdateScreen_DoNothing; } d->ptr.counter += 1; DataModelRefMut_delete(&d); return UpdateScreen_RegenerateDomForCurrentWindow; }

int main() { DataModel model = { .counter = 5 }; AzApp app = AzApp_new(DataModel_upcast(model), AzAppConfig_default()); AzApp_run(app, AzWindowCreateOptions_new(render_my_view)); return 0; }


The documentation is built using the
script, which will generate the entire
website in the
python3 ./

NOTE: The class documentation can also be printed as a PDF if you prefer that.


This library is licensed under the LGPL version 3.0 with an exception for static linking. Which means that similar to the FLTK and wxWidgets license, you can build proprietary applications without having to publish your source code: you only have to publish changes made to the azul library itself. The static linking exception allows you to statically link Azul without having to publish your code.

Copyright 2017 - current Felix Schütt

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.