Media

Image

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

Open ↗
Image interaction recorded with the Blinc headless renderer
Hybrid · dark · native recording · 2×

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.

PropertyTypeUpdate modelThis example
sourcedata URIConstructionEmbedded landscape image
coverObjectFitConstructionCrop 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

  1. 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.
  2. When an image first intersects the viewport, the loader is triggered and a placeholder is drawn at the image's final layout rect.
  3. 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_ms is the alpha multiplier — once it reaches 1.0 the image is fully opaque and the fade-in flag is cleared.
  4. 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.
  5. 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

StrategyDescription
Eager (default)Load and decode immediately when the element is created
LazyDefer load until the image's layout rect intersects the viewport

Placeholder types

PlaceholderDescription
NoneNo 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
SkeletonShimmer 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;
}
PropertyValuesEffect
loadingeager | lazySwitches the loading strategy
image-placeholder-typenone | color | skeletonSelects 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 Stateful and 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.