Skip to content

v0.6.0-alpha.2

Pre-release
Pre-release
Compare
Choose a tag to compare
@ealmloff ealmloff released this 07 Aug 23:39
· 110 commits to main since this release
506d47f

Dioxus 0.6.0 Alpha

Dioxus 0.6 includes improvements to several areas of dioxus. The highlights are:

  • Asset improvements
  • Head elements
  • RSX autocomplete
  • Improved hot reloading
  • Suspense and streaming
  • Error boundaries
  • A new TUI!
  • Documentation improvements
  • Blitz rewrite

Asset Stabilization

We introduced our new asset system, Manganis, in an alpha state with the 0.5 release. Dioxus 0.6 stabilizes the asset system and fixes several bugs and performance issues. You can try out the new linker based asset system by including an asset! anywhere in your code. It will automatically be optimized and bundled across all platforms:

rsx! {
    img { src: asset!("./assets/myimg.png") }
}

Head Elements

In addition to the Manganis asset system, dioxus 0.6 includes a new way to include assets into your html. The new Script, Link, Style , and Meta components let you link to assets anywhere in your html. These components will automatically be hoisted into the <head> across all platforms and deduplicated by the source:

#[component]
fn MyStyledComponent() -> {
    rsx! {
        head::Link {
            rel: "stylesheet",
            href: asset!("./assets/style.css")
        }
        "This component is styled"
    }
}

Autocomplete and improved errors for RSX

RSX now supports autocomplete everywhere. In addition to expressions, elements, components and attributes now autocomplete correctly:

autocomplete.mov

The new version of RSX also features better error messages for incorrect markup:

Incorrect markup error message

Supercharged Hot Reloading

In 0.6, RSX hot reloading is much more consistent. You can move around and duplicate expressions anywhere in an rsx block or create new literals anywhere in an rsx block. This means you can now create new formatted text nodes, new formatted attributes or tweak properties on the fly without recompiling!

hot-reloading.mov

Suspense and Streaming

Async is a core component of any UI framework. Dioxus provides hooks to handle async state. You can start a future and handle the loading and resolved states within the component:

#[component]
fn Article() -> Element {
    // Use resource starts a future in the background and returns the current state
    let article = use_resource(fetch_article);

    rsx! {
        // You can match the state of the future to render either a loading state or the resolved state
        match article() {
            Some(article) => rsx! { "{article}" },
            None =>  rsx! { p { "Loading..." } }
        }
    }
}

This works ok if you have a single future, but it quickly gets messy when combining many futures into one UI:

#[component]
fn Article() -> Element {
    // Use resource starts a future in the background and returns the current state
    let Some(title) = use_resource(fetch_title).cloned() else {
         return rsx! { "loading..." }
    };
    let Some(article) = use_resource(fetch_article).cloned() else {
         return rsx! { "loading..." }
    };
    let Some(category) = use_resource(move || article.title()).cloned() else {
         return rsx! { "loading..." }
    };

    rsx! {
        Title { "{title}" }
        Body { category, article }
    }
}

In addition to hooks, we need a way to display a different state when async is loading. Dioxus 0.6 introduces a new core primitive for async UI called suspense boundaries. A suspense boundary is a component that renders a placeholder when any child component is loading:

rsx! {
    SuspenseBoundary {
        fallback: |context: SuspenseContext| rsx! {
                // Render a loading placeholder if any child component is suspended
                "Loading..."
        },
        Article {}
    }
}

In any child component, you can just bubble up the pending state with ? to pause rendering until the future is finished:

#[component]
fn Article() -> Element {
    let title = use_resource(fetch_title).suspend()?;
    let article = use_resource(fetch_article).suspend()?;
    let category = use_resource(move || article.title()).suspend()?;

    // Since we bubbled up all the pending futures with `?` we can just
    // handle the happy path in the component
    rsx! {
        Title { "{title}" }
        Body { category, article }
    }
}

Along with suspense boundaries, dioxus fullstack also supports streaming each suspense boundary in from the server. Instead of waiting for the whole page to load, dioxus fullstack streams in each chunk with the resolved futures as they finish:

streaming.mov

Error boundaries

0.6 also introduces error boundaries which let you display an error message if any child component runs into an error:

#[component]
fn Root() -> Element {
    rsx! {
        ErrorBoundary {
            handle_error: |errors: ErrorContext| {
                match errors.show() {
                    // Render the view the child component through with the error
                    Some(view) => view,
                    // Or a fallback if the error doesn't come with a view
                    None => rsx! {
                        pre {
                            color: "red",
                            "oops, we ran into an error\n{errors:#?}"
                        }
                    }
                }
            },
            Contents {}
        }
    }
}

You can throw an error from any child component while rendering or handling events:

#[component]
fn Double(input: String) -> Element {
    // You can use ? to throw an event during rendering
    let parsed: f32 = input.parse()?;
    let doubled = parsed * 2.0;
    
    rsx! {
        "{doubled}"
    }
}

#[component]
fn DoubleButton(mut count: Signal<u8>) -> Element {
    rsx! {
        button {
            onclick: move |_| {
                // Errors can have an associated view which the error boundary can
                // choose to show
                let doubled = count().checked_mul(2).show(|_|
                    rsx! { "multiplying {count} by 2 would overflow the u8" }
                )?;
                count.set(doubled);

                Ok(())
            },
            "Double!"
        }
    }
}

DX TUI

The Dioxus CLI has been rewritten to support a TUI with build progress, and multiple views for logs. It has shortcuts for common actions, and displays progress in both the TUI and the site as the project loads:

tui.mov

Blitz Rewrite

Blitz has been rewritten to use Firefox's browser engine, Stylo. The new version of Blitz is much more capable with proper accessibility support, IME support, and better text support. Blitz should be a drop in replacement for dioxus desktop for the small subset of HTML it supports. Keep in mind Blitz is still experimental and not ready for production use. You can try Blitz by adding dioxus_blitz from the git repo:

cargo add dioxus-blitz --git https://github.com/DioxusLabs/blitz

Launching your Dioxus app with the blitz renderer:

dioxus_blitz::launch(app);

Dioxus with Blitz

Ergonomic tweaks

Dioxus 0.6 also includes a number of smaller ergonomic tweaks that should help dioxus feel a lot more consistent

Static Generation

Dioxus 0.6 splits out static generation into its own platform to make it easier to set up:

//! Static generation works out of the box with the router. Just add a router anywhere in your app and it will generate any static routes for you!

#![allow(unused)]
use dioxus::prelude::*;

// Generate all routes and output them to the static path
fn main() {
    launch(|| {
        rsx! {
            Router::<Route> {}
        }
    });
}

#[derive(Clone, Routable, Debug, PartialEq)]
enum Route {
    #[route("/")]
    Home {},

    #[route("/blog")]
    Blog,
}

#[component]
fn Blog() -> Element {
    rsx! {
        Link { to: Route::Home {}, "Go to counter" }
        table {
            tbody {
                for _ in 0..100 {
                    tr {
                        for _ in 0..100 {
                            td { "hello!" }
                        }
                    }
                }
            }
        }
    }
}

#[component]
fn Home() -> Element {
    let mut count = use_signal(|| 0);

    rsx! {
        Link { to: Route::Blog {}, "Go to blog" }
        div {
            h1 { "High-Five counter: {count}" }
            button { onclick: move |_| count += 1, "Up high!" }
            button { onclick: move |_| count -= 1, "Down low!" }
        }
    }
}

Fullstack State

Fullstack state now propagates from the launch builder into server functions which makes it much easier to set up state that is shared throughout your application like a database pool:

fn main() {
     LaunchBuilder::new().with_context(1234567890u32).launch(app);
}

#[server]
async fn get_server_data() -> Result<String, ServerFnError> {
     let FromContext(context): FromContext<u32> = extract().await?;
     Ok(format!("the context was {context}"))
}

Callbacks

Dioxus 0.6 expands EventHandlers into callbacks which let you both take and return arguments. Callbacks are a Copy version of Box<dyn FnMut(Args) -> Ret> built specifically for UI in Rust

#[component]
fn DoubleButton(double: Callback<u32, u32>) -> Element {
    let mut count = use_signal(|| 1);

    rsx! {
        button {
            // Callbacks let you easily inject custom logic into your components
            onclick: move |_| count.set(double(count())),
            "{count}"
        }
    }
}

Improved warnings

Dioxus 0.6 also expands the warning system to include hints for several problematic behaviors. Dioxus now warns if components are called as functions, state is passed up the component tree, or a component rerun is triggered while rendering.

Hoisted value warning

Improved Inline Documentation

We also improved the inline documentation throughout the dioxus crates. Many common items now include in depth examples, explanations, and help for common errors. For comparison, here is the documentation in 0.5 and 0.6 for oninput:

and use_memo:

Bug fixes

Dioxus 0.6 also includes a plethora of bug fixes and consistency tweaks. Over 200 issues closed with 100 bugs fixed

Screenshot 2024-08-07 at 9 49 42 AM

Call for Testing

Testing the alpha release and opening issues for any bugs you run into is a great way to contribute back to dioxus!

Feel free to hop into the discord to give feedback and/or chat about the new changes.

Full Change Log

New Contributors!

Full Diff: v0.5.1...v0.6.0-alpha.1