The Dioxus VirtualDom can be rendered server-side.
The Dioxus VirtualDom, sadly, is not currently Send
. Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc.
To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels.
When working with web frameworks that require Send
, it is possible to render a VirtualDom immediately to a String – but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a pool of VirtualDoms.
If you just want to render rsx!
or a VirtualDom to HTML, check out the API docs. It's pretty simple:
// We can render VirtualDoms
let mut vdom = VirtualDom::new(app);
let _ = vdom.rebuild();
println!("{}", dioxus_ssr::render_vdom(&vdom));
// Or we can render rsx! calls directly
println!( "{}", dioxus_ssr::render_lazy(rsx! { h1 { "Hello, world!" } } );
However, for this guide, we're going to show how to use Dioxus SSR with Axum
.
Make sure you have Rust and Cargo installed, and then create a new project:
cargo new --bin demo
cd app
Add Dioxus with the ssr
feature:
cargo add dioxus --features ssr
Next, add all the Axum dependencies. This will be different if you're using a different Web Framework
cargo add tokio --features full
cargo add axum
Your dependencies should look roughly like this:
[dependencies]
axum = "0.4.5"
dioxus = { version = "*", features = ["ssr"] }
tokio = { version = "1.15.0", features = ["full"] }
Now, set up your Axum app to respond on an endpoint.
use axum::{response::Html, routing::get, Router};
use dioxus::prelude::*;
#[tokio::main]
async fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(
Router::new()
.route("/", get(app_endpoint))
.into_make_service(),
)
.await
.unwrap();
}
And then add our endpoint. We can either render rsx!
directly:
async fn app_endpoint() -> Html<String> {
Html(dioxus_ssr::render_lazy(rsx! {
h1 { "hello world!" }
}))
}
Or we can render VirtualDoms.
async fn app_endpoint() -> Html<String> {
fn app(cx: Scope) -> Element {
cx.render(rsx!(h1 { "hello world" }))
}
let mut app = VirtualDom::new(app);
let _ = app.rebuild();
Html(dioxus_ssr::render_vdom(&app))
}
And that's it!
You might notice that you cannot hold the VirtualDom across an await point. Dioxus is currently not ThreadSafe, so it must remain on the thread it started. We are working on loosening this requirement.