dioxus-compose guide

Images and backgrounds

The app decides what an image is

dioxus-compose never fetches anything. An <img src> or a CSS url() is recorded as written, and an image resolver you supply says how big it is and which asset draws it. Gradients need no resolver, because they are computed from the CSS alone.

The image resolver

An app may already have its images in memory, may want to fetch them itself, or may want to refuse some. So loading is the app's decision, made through one trait:

dioxus_compose::html
pub trait ImageResolver {
    /// The asset that draws the image at `url`, or None to draw nothing.
    /// Asked while a plan is built, never during layout.
    fn resolve(&mut self, url: &str) -> Option<AssetId>;

    /// The natural size, when the app knows it without fetching anything.
    /// Asked during layout. None, the default, leaves the image without one.
    fn size(&mut self, _url: &str) -> Option<ImageSize> {
        None
    }
}

AssetId is your own number for an image you registered with the renderer; it is handed back in the plan and never looked into. ImageSize is in CSS pixels, so an image made for a screen of two device pixels per CSS pixel is half its pixel size here.

The resolver goes in HtmlConfig::images, or later through HtmlDom::set_image_resolver. Without one, an <img> takes only the size its attributes and CSS give it, and no image draws anything.

samples/support/src/lib.rs, trimmed: a resolver that knows a fixed list
impl ImageResolver for Images {
    fn resolve(&mut self, url: &str) -> Option<AssetId> {
        self.find(url).map(|(_, _, asset)| *asset)
    }

    fn size(&mut self, url: &str) -> Option<ImageSize> {
        self.find(url).map(|(_, size, _)| *size)
    }
}

URLs

HtmlConfig::base_url is what relative src attributes and CSS url() values resolve against. Without one, or with one that nothing can be resolved against, such as a data: URL, a relative URL reaches your resolver exactly as it was written: photos/tram.jpg stays photos/tram.jpg.

The img element

An <img> with no size of its own is laid out at the size the resolver gave. object-fit decides where inside its box the picture is drawn. In samples/gallery the 48 by 48 avatar keeps its natural size, the photos cover their tiles, and the route map is shown whole with contain.

samples/gallery/src/lib.rs
.avatar { border-radius: 50%; }

.tile {
    margin: 0;
    border-radius: 8px;
    overflow: hidden;
}

.tile img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.tile img.whole { object-fit: contain; }

Background images

background-image layers keep their CSS order: the first is drawn on top, and all of them over the background colour. background-size (cover, contain or lengths), background-position and background-repeat are resolved into rectangles, so the renderer draws an image into a box rather than interpreting CSS.

samples/gallery/src/lib.rs
.postcard {
    background-image: url(textures/postcard.png);
    background-size: cover;
    background-position: center;
}

Gradients

linear-gradient(), radial-gradient() and their repeating forms are resolved against the tile they fill. Keywords such as to right become the angle they mean for that tile's size, and every stop gets its offset and colour. The gallery header is one gradient:

samples/gallery/src/lib.rs
.hero {
    display: flex;
    align-items: center;
    gap: 16px;
    height: 160px;
    padding: 0 32px;
    background-image: linear-gradient(to right, #4f46e5, #06b6d4);
    color: #ffffff;
}

Changing one stop's colour is one change in the plan, the same as changing a background colour.

Not yet