Styling and Assets
Unfortunately, our HotDog app isn't quite ready to show off - it's completely unstyled!
In this chapter we'll cover adding assets and styles to our app.
Dioxus uses CSS for Styling
As mentioned earlier, Dioxus apps use HTML and CSS as the core markup and styling technology. Instead of re-inventing the wheel like Flutter and React-Native, we designed Dioxus to use HTML and CSS on every platform.
CSS is by-far the most popular styling system and is extremely capable. For example, here's a screenshot of ebou, a very beautiful Mastodon client built with Dioxus.
HTML and CSS are very powerful - don't worry about being too limited!
Adding the CSS File with asset!()
The bare-bones template already includes a base main.css
in the assets
folder.
├── Cargo.toml ├── assets │ └── main.css └── src └── main.rs
To include the CSS in our app, we can use the asset!()
macro. This macro ensures the asset will be included in the final app bundle.
static CSS: Asset = asset!("/assets/main.css");
We also need to load the asset into our app using the document::Stylesheet
component. This component is equivalent to the <link>
HTML element but also ensures the CSS will be pre-loaded during server-side-rendering.
fn app() -> Element { rsx! { document::Stylesheet { href: CSS } // rest of the app } }
Unlike Rust's include_str!()
macro, the asset!()
macro does not actually include the contents of the asset in our final executable. Instead, it generates a unique path so that the asset can be loaded at runtime. This is ideal for web apps where assets are loaded in parallel through different HTTP requests.
📣 The
asset!()
macro generates a unique name that won't exactly match the input name. This helps prevents name collisions and improves caching.
Hot-Reloading
All assets in Dioxus participate in hot-reloading. Try editing your app's main.css
and watch changes propagate in real time.
Including Images
In Dioxus, you can include images in two ways:
- Dynamically with a URL
- Statically with the
asset!()
macro.
When including assets with a URL, simply fill the src
attribute of img {}
. Note that when the app is offline, URL-based images won't download.
rsx! { // ... div { img { src: "https://images.dog.ceo/breeds/pitbull/dog-3981540_1280.jpg" } } // ... }
For static images, you can use the same asset!()
macro that we used to include the app's CSS.
static ICON: Asset = asset!("/assets/icon.png"); rsx! { img { src: ICON } }
Optimizations
By default, the asset!()
macro will lightly optimize CSS, JavaScript, JSON, and images. The name of the asset will also be modified to include a content hash.
// would output main-j1238nask123.css asset!("/assets/main.css").to_string()
You can optimize assets even further, with an optional Options
struct. For example, dx
can automatically convert .png
images to a more optimized .avif
format:
// outputs image-j1238jd2.avif asset!("/assets/image.png", ImageAssetOptions::new().with_avif())
For many apps, asset optimization is the most effective way of improving load times. As developers, we frequently overlook the size of images and accidentally make our sites load slower.
Check out the assets guide for a more in-depth explanation of how the Dioxus asset system works.
The Final CSS
We can use the asset hot-reload system of dx
and our knowledge of CSS to create a beautiful app:
The final CSS is here for reference:
/* App-wide styling */ html, body { background-color: #0e0e0e; color: white; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; height: 100%; width: 100%; overflow: hidden; margin: 0; } #main { display: flex; flex-direction: column; height: 100%; justify-content: space-between; } #dogview { max-height: 80vh; flex-grow: 1; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; } #dogimg { display: block; max-width: 50%; max-height: 50%; transform: scale(1.8); border-radius: 5px; border: 1px solid rgb(233, 233, 233); box-shadow: 0px 0px 5px 1px rgb(216, 216, 216, 0.5); } #title { text-align: center; padding-top: 10px; border-bottom: 1px solid #a8a8a8; display: flex; flex-direction: row; justify-content: space-evenly; align-items: center; } #title a { text-decoration: none; color: white; } #heart { background-color: white; padding: 5px; border-radius: 5px; } #title span { width: 20px; } #title h1 { margin: 0.25em; font-style: italic; } #buttons { display: flex; flex-direction: row; justify-content: center; gap: 20px; /* padding-top: 20px; */ padding-bottom: 20px; } #skip { background-color: gray } #save { background-color: green; } #skip, #save { padding: 5px 30px 5px 30px; border-radius: 3px; font-size: 2rem; font-weight: bold; color: rgb(230, 230, 230) } #navbar { border: 1px solid rgb(233, 233, 233); border-width: 1px 0px 0px 0px; display: flex; flex-direction: row; justify-content: space-evenly; padding: 20px; gap: 20px; } #navbar a { background-color: #a8a8a8; border-radius: 5px; border: 1px solid black; text-decoration: none; color: black; padding: 10px 30px 10px 30px; } #favorites { flex-grow: 1; overflow: hidden; display: flex; flex-direction: column; padding: 10px; } #favorites-container { overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 10px; padding: 10px; } .favorite-dog { max-height: 180px; max-width: 60%; position: relative; } .favorite-dog img { max-height: 150px; border-radius: 5px; margin: 5px; } .favorite-dog:hover button { display: block; } .favorite-dog button { display: none; position: absolute; bottom: 10px; left: 10px; z-index: 10; }