Image
Display raster images with fitting, cropping and theme-aware surfaces.

use blinc_layout::prelude::*;
pub fn image_demo(viewport: (f32, f32)) -> Div {
div().class("demo-wide").child(
div().class("demo-image").child(
img(include_str!("../../assets/avatar.data-uri"))
.w((viewport.0 - 64.0).clamp(1.0, 420.0))
.h(216.0)
.cover()
.no_fade(),
),
)
}
Usage and properties
The image is embedded, so native and live previews use the same asset.
Use the installation guide to create a Blinc application. This widget is available from blinc_layout; the demo uses the same Hybrid theme as the component library. Create the fixture bindings once, and return the demo from your UI builder. Values marked Bound accept Signal/Computed sources where the API supports them; structural options configure the component at construction.
| Property | Type | Update model | This example |
|---|---|---|---|
source | data URI | Construction | Embedded landscape image |
cover | ObjectFit | Construction | Crop to fill the container |
Keyboard and interaction
This preview displays content without a keyboard action.
Customizing it
Add application CSS after the default component layer. These selectors are the restyling surface for this component:
.demo-image
Theme and component variables: --primary, --text-primary, --text-secondary, --surface, --border, --radius-default, --duration-fast, --ease-state.
Built-in themes
The Live theme control installs Blinc's own theme bundles, including their colours, focus rings, corner shapes and motion tokens.
pub fn theme_bundle_for(name: &str) -> blinc_theme::ThemeBundle {
match name {
"restrained" => blinc_theme::RestrainedTheme::bundle(),
"expressive" => blinc_theme::ExpressiveTheme::bundle(),
_ => blinc_theme::HybridTheme::bundle(),
}
.with_css(blinc_cn::cn_styles::CN_STYLES)
}Download the default widget and component stylesheets. Sample assets and licenses are available in the asset guide.
Documentation
From the canonical component guide.
Basic Image
use blinc::layout::image::image;
image("path/to/photo.png")
.w(200.0)
.h(150.0)
Image from URL
image("https://example.com/image.jpg")
.w(300.0)
.h(200.0)
Object Fit
Control how the image fills its container:
image(src)
.w(200.0)
.h(200.0)
.cover() // Fill container, crop if needed (default)
image(src)
.contain() // Fit entirely, may letterbox
image(src)
.fill() // Stretch to fill exactly
image(src)
.scale_down() // Scale down only if larger
image(src)
.no_scale() // No scaling, original size
Object Position
Control alignment within the container:
image(src)
.cover()
.center() // Center (default)
image(src)
.cover()
.top_left()
image(src)
.cover()
.top_center()
image(src)
.cover()
.bottom_right()
// Custom position (0.0 to 1.0)
image(src)
.cover()
.position_xy(0.25, 0.75)
Image Filters
image(src)
.w(200.0)
.h(200.0)
.grayscale(0.5) // 0.0 = color, 1.0 = grayscale
.sepia(0.3) // Sepia tone
.brightness(1.2) // > 1.0 brighter, < 1.0 darker
.contrast(1.1) // > 1.0 more contrast
.saturate(0.8) // < 1.0 less saturated
.hue_rotate(45.0) // Rotate hue (degrees)
.invert(0.2) // Color inversion
.blur(2.0) // Blur radius
Lazy Loading
For content-heavy apps with many images (galleries, feeds, chat) lazy loading defers decode until the image is actually visible in the viewport. While the real bitmap is loading, Blinc renders a placeholder in its place; once the texture lands in cache, the image fades in over a configurable duration.
How it works
- On each frame, the renderer tests every lazy image's quad against the current viewport AABB. Off-screen images are skipped — no decode, no GPU upload.
- When an image first intersects the viewport, the loader is triggered and a placeholder is drawn at the image's final layout rect.
- As soon as the decoded texture appears in the GPU cache, Blinc records
image_load_times[source] = Instant::now()and starts the fade-in.elapsed_ms / fade_duration_msis the alpha multiplier — once it reaches1.0the image is fully opaque and the fade-in flag is cleared. - Placeholder images (type 2) are eagerly preloaded so they're available the moment the lazy element appears — there's no flash of empty space waiting on the thumbnail itself.
- While any image is fading in, the runtime keeps requesting redraws so the animation runs smoothly even when nothing else on screen is changing.
Builder API
use blinc::layout::prelude::*;
use std::time::Duration;
// Basic lazy loading — no placeholder, just defer decode
img("large-photo.jpg")
.lazy()
.w(300.0)
.h(200.0)
// Solid color placeholder
img("photo.jpg")
.lazy()
.placeholder_color(Color::rgba(0.2, 0.2, 0.2, 1.0))
.w(300.0)
.h(200.0)
// Gradient (or any Brush) placeholder
img("photo.jpg")
.lazy()
.placeholder_brush(Brush::Gradient(Gradient::linear(
Point::new(0.0, 0.0),
Point::new(1.0, 1.0),
Color::rgba(0.4, 0.6, 1.0, 1.0),
Color::rgba(0.6, 0.4, 1.0, 1.0),
)))
.w(300.0)
.h(200.0)
// Thumbnail / blur-hash placeholder — eagerly preloaded
img("large-photo.jpg")
.lazy()
.placeholder_image("thumbnail.jpg")
.fade_in(Duration::from_millis(300))
.w(300.0)
.h(200.0)
// Skeleton shimmer (animated band sweeping left → right)
img("photo.jpg")
.lazy()
.skeleton()
.fade_in(Duration::from_millis(250))
.w(300.0)
.h(200.0)
// Cross-fade off — image pops in instantly when ready
img("photo.jpg")
.lazy()
.no_fade()
.w(300.0)
.h(200.0)
Loading strategies
| Strategy | Description |
|---|---|
Eager (default) | Load and decode immediately when the element is created |
Lazy | Defer load until the image's layout rect intersects the viewport |
Placeholder types
| Placeholder | Description |
|---|---|
None | No placeholder — empty until the bitmap arrives |
Color(color) | Solid color background |
Brush(brush) | Any brush — gradients, glass effects, etc. |
Image(url) | Another image (low-res thumbnail, blur hash). Preloaded on tree build |
Skeleton | Shimmer band animation, no asset required |
CSS overrides
Lazy-loading behavior can be overridden from a stylesheet without touching the builder. This is useful for theming galleries or applying defaults to all images that match a class.
.gallery-item {
loading: lazy;
image-placeholder-type: skeleton;
fade-duration: 250ms;
}
.avatar {
loading: lazy;
image-placeholder-color: rgba(40, 40, 50, 1.0);
fade-duration: 200ms;
}
.hero {
loading: lazy;
image-placeholder-image: "hero-blur.jpg";
fade-duration: 400ms;
}
| Property | Values | Effect |
|---|---|---|
loading | eager | lazy | Switches the loading strategy |
image-placeholder-type | none | color | skeleton | Selects which placeholder to draw |
image-placeholder-color | <color> | Solid color placeholder; implies type: color |
image-placeholder-image | <url> | Thumbnail placeholder; implies type: image |
fade-duration | <time> (200ms, 0.3s) | Fade-in length once the bitmap is ready |
CSS values override builder values. Use the builder for one-off images and CSS for repeated patterns.
Note: Lazy loading currently applies to raster images only. SVGs are vectorized and rasterized on demand, so deferring their decode rarely pays for itself; if you need a deferred SVG, wrap it in a
Statefuland reveal it when needed.
Platforms and version
The component API is available in Blinc 0.6.0. The Live example runs in a dedicated worker and requires a compatible GPU surface. Preview and Code remain available when Live cannot start. The canvas has a descriptive label; a full screen-reader DOM mirror is not provided. The gallery integration is based on revision e2e55c24e078.
Recordings and live demos use the default Hybrid theme with bundled Noto Sans. The recording uses a 560 × 320 logical viewport at twice that resolution. Live follows its container and actual display density. Source base revision · Exact build source · Artifact versions and checksums · Font license.