Server Side Components
The Dioxus framework gives us the capability to build user interfaces out of components which can be rendered server side. It's worth looking at their components documentation.
Creating a ui-components crate
cargo init --lib crates/ui-components
Install Dioxus
cd crates/ui-components
cargo add [email protected] --features ssr
Creating a Layout Component
A layout defines the surroundings of an HTML page. It's the place to define a common look and feel of your final output.
create a file called crates/ui-components/src/layout.rs
.
#![allow(non_snake_case)]
use dioxus::prelude::*;
#[derive(Props)]
pub struct AppLayoutProps<'a> {
title: &'a str,
children: Element<'a>,
}
pub fn Layout<'a>(cx: Scope<'a, AppLayoutProps<'a>>) -> Element {
cx.render(rsx!(
{
LazyNodes::new(|f| f.text(format_args!("<!DOCTYPE html><html lang='en'>")))
}
head {
title {
"{cx.props.title}"
}
meta {
charset: "utf-8"
}
meta {
"http-equiv": "X-UA-Compatible",
content: "IE=edge"
}
meta {
name: "viewport",
content: "width=device-width, initial-scale=1"
}
}
body {
&cx.props.children
}
))
}
Let's use this layout to create a very simple users screen that will show a table of users.
Make sure you're in the crates/ui-components
folder and add the db
crate to your Cargo.toml
using the following command:
cargo add --path ../db
Create a file crates/ui-components/src/users.rs
.
use crate::layout::Layout;
use db::User;
use dioxus::prelude::*;
struct Props {
users: Vec<User>
}
pub fn users(users: Vec<User>) -> String {
fn app(cx: Scope<Props>) -> Element {
cx.render(rsx! {
Layout { title: "Users Table",
table {
thead {
tr {
th { "ID" }
th { "Email" }
}
}
tbody {
cx.props.users.iter().map(|user| rsx!(
tr {
td {
strong {
"{user.id}"
}
}
td {
"{user.email}"
}
}
))
}
}
}
})
}
let mut app = VirtualDom::new_with_props(
app,
Props {
users
},
);
let _ = app.rebuild();
dioxus::ssr::render_vdom(&app)
}
If we update our crates/ui-components/src/lib.rs
to look like the following...
mod layout;
pub mod users;
Then finally we can change our axum-server
code to generate HTML rather than JSON.
Make sure you're in the crates/axum-server
folder and add the ui-components
crate to your Cargo.toml
using the following command:
cargo add --path ../ui-components
Update crates/axum-server/src/main.rs
mod config;
mod errors;
use crate::errors::CustomError;
use axum::{response::Html, extract::Extension, routing::get, Router};
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
let config = config::Config::new();
let pool = db::create_pool(&config.database_url);
let app = Router::new()
.route("/", get(users))
.layer(Extension(config))
.layer(Extension(pool.clone()));
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
println!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await.unwrap();
}
async fn users(Extension(pool): Extension<db::Pool>) -> Result<Html<String>, CustomError> {
let client = pool.get().await?;
let users = db::queries::users::get_users()
.bind(&client)
.all()
.await?;
Ok(Html(ui_components::users::users(
users,
)))
}
You should get results like the screenshot below.
