This guide assumes you read the Web guide and installed the Dioxus-cli
Getting Started
Setup
For this guide, we're going to show how to use Dioxus with Axum, but dioxus-fullstack
also integrates with the Warp and Salvo web frameworks.
Make sure you have Rust and Cargo installed, and then create a new project:
cargo new --bin demo
cd demo
Add dioxus
and dioxus-fullstack
as dependencies:
cargo add dioxus
cargo add dioxus-fullstack --features axum, 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 = "*"
dioxus = { version = "*" }
dioxus-fullstack = { version = "*", features = ["axum", "ssr"] }
tokio = { version = "*", features = ["full"] }
Now, set up your Axum app to serve the Dioxus app.
#![allow(non_snake_case, unused)] use dioxus::prelude::*; #[tokio::main] async fn main() { #[cfg(feature = "ssr")] { use dioxus_fullstack::prelude::*; let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); axum::Server::bind(&addr) .serve( axum::Router::new() .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) .into_make_service(), ) .await .unwrap(); } } fn app(cx: Scope) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx! { h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } }) }
Now, run your app with cargo run
and open http://localhost:8080
in your browser. You should see a server-side rendered page with a counter.
Hydration
Right now, the page is static. We can't interact with the buttons. To fix this, we can hydrate the page with dioxus-web
.
First, modify your Cargo.toml
to include two features, one for the server called ssr
, and one for the client called web
.
[dependencies]
# Common dependancies
dioxus = { version = "*" }
dioxus-fullstack = { version = "*" }
# Web dependancies
dioxus-web = { version = "*", features=["hydrate"], optional = true }
# Server dependancies
axum = { version = "0.6.12", optional = true }
tokio = { version = "1.27.0", features = ["full"], optional = true }
[features]
default = []
ssr = ["axum", "tokio", "dioxus-fullstack/axum"]
web = ["dioxus-web"]
Next, we need to modify our main.rs
to use either hydrate on the client or render on the server depending on the active features.
#![allow(non_snake_case, unused)] use dioxus::prelude::*; fn main() { #[cfg(feature = "web")] dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true)); #[cfg(feature = "ssr")] { use dioxus_fullstack::prelude::*; tokio::runtime::Runtime::new() .unwrap() .block_on(async move { let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); axum::Server::bind(&addr) .serve( axum::Router::new() .serve_dioxus_application("", ServeConfigBuilder::new(app, ())) .into_make_service(), ) .await .unwrap(); }); } } fn app(cx: Scope) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx! { h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } }) }
Now, build your client-side bundle with dioxus build --features web
and run your server with cargo run --features ssr
. You should see the same page as before, but now you can interact with the buttons!
Sycronizing props between the server and client
Let's make the initial count of the counter dynamic based on the current page.
Modifying the server
To do this, we must remove the serve_dioxus_application and replace it with a custom implementation of its four key functions:
- Serve static WASM and JS files with serve_static_assets
- Register server functions with register_server_fns (more information on server functions later)
- Connect to the hot reload server with connect_hot_reload
- A custom route that uses SSRState to server-side render the application
Modifying the client
The only thing we need to change on the client is the props. dioxus-fullstack
will automatically serialize the props it uses to server render the app and send them to the client. In the client section of main.rs
, we need to add get_root_props_from_document
to deserialize the props before we hydrate the app.
#![allow(non_snake_case, unused)] use dioxus::prelude::*; use dioxus_fullstack::prelude::*; fn main() { #[cfg(feature = "web")] dioxus_web::launch_with_props( app, // Get the root props from the document get_root_props_from_document().unwrap_or_default(), dioxus_web::Config::new().hydrate(true), ); #[cfg(feature = "ssr")] { use axum::extract::Path; use axum::extract::State; use axum::routing::get; tokio::runtime::Runtime::new() .unwrap() .block_on(async move { let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080)); axum::Server::bind(&addr) .serve( axum::Router::new() // Serve the dist folder with the static javascript and WASM files created by the dixous CLI .serve_static_assets("./dist") // Register server functions .register_server_fns("") // Connect to the hot reload server in debug mode .connect_hot_reload() // Render the application. This will serialize the root props (the intial count) into the HTML .route( "/", get(move | State(ssr_state): State<SSRState>| async move { axum::body::Full::from( ssr_state.render( &ServeConfigBuilder::new( app, 0, ) .build(), ) )}), ) // Render the application with a different intial count .route( "/:initial_count", get(move |Path(intial_count): Path<usize>, State(ssr_state): State<SSRState>| async move { axum::body::Full::from( ssr_state.render( &ServeConfigBuilder::new( app, intial_count, ) .build(), ) )}), ) .with_state(SSRState::default()) .into_make_service(), ) .await .unwrap(); }); } } fn app(cx: Scope<usize>) -> Element { let mut count = use_state(cx, || *cx.props); cx.render(rsx! { h1 { "High-Five counter: {count}" } button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } }) }
Now, build your client-side bundle with dioxus build --features web
and run your server with cargo run --features ssr
. Navigate to http://localhost:8080/1
and you should see the counter start at 1. Navigate to http://localhost:8080/2
and you should see the counter start at 2.