In Reflex, images aren't widgets. They're layers painted into the bg: (or fg:) of any styled object, inheriting the same states, transitions, layout, and hover hooks as everything else.
Conventions used in every diagram:
Before any Image(source: ...) resolves, the file must live somewhere the build can bundle, and a @Bitmap declaration must point at it. In a Reflex C++ app, every bundled asset goes under the app's resources/ folder:
MyApp/
├── code/
├── resources/
│ ├── styles.glx
│ ├── icons/
│ └── images/
└── resources.xml
The two ways to write a path:
Inside @Bitmap, path: accepts two forms.
A bare relative path resolves against the current stylesheet's bundled resources folder. Common when the image lives in your own app:
@Bitmap piece_wp: { path: "pieces/Chess_plt45.png"; antialias: true; };
A fully-qualified :res: path spells out the namespace prefix. Use it for cross-namespace or SDK-provided resources:
@Bitmap logo: { path: ":res:myapp/images/logo.png"; };
@Font Title: { path: ":res:expenses/fonts/Satoshi-Bold.otf"; size: 18; };
Both forms work for @Bitmap and @Font.
The @Bitmap declaration parameters
Putting it all together:
@Bitmap logo:
{
path: "images/logo.png"; /* required: relative or :res: path */
density: 2; /* optional: Retina density factor */
antialias: true; /* optional: smooth scaling */
tile: 24; /* optional: sprite-sheet tile size */
};
Reference it later by the declared name:
Header: { bg: Image(source: logo; fit: contain); };
fit: modesThe fit: parameter controls how the image sizes inside its object's bounds. Three values: cover, contain, stretch.
fit: cover
Image fills the bounds, cropping if needed. Same idea as CSS background-size: cover. The image scales up so both axes fully cover the object's bounds. Whatever overflows the longer axis is clipped.
LeftPanel:
{
size: 360, auto;
bg:
Fill(colour: black),
Image(source: left_bg; fit: cover); /* background image fills the panel */
};
When to use: hero panels, full-bleed backgrounds, anywhere you want the image to fill the area without distortion and don't mind cropping the edges.
fit: contain
Image fits entirely within the bounds, no cropping. Same idea as CSS background-size: contain. The shorter axis leaves spare room. Use anchor: (covered below) to position the image within that room.
PlayPauseButton:
{
size: 28, 28;
bg: Image(source: play_icon; fit: contain);
};
When to use: icons, logos, anything where you want the whole image visible and you control the container size.
fit: stretch
Image distorts to fill the bounds exactly. The image is non-uniformly scaled so both axes match the bounds exactly. Aspect ratio is not preserved.
DistortedBackground:
{
bg: Image(source: noise_tile; fit: stretch);
};
When to use: rarely. Mostly for textures or noise patterns where aspect doesn't matter. For everything else prefer cover or contain.
anchor: parameterWith fit: contain, the image leaves spare room on the longer axis. anchor: chooses where in that room the image sits. Values match the nine-point alignment grid:
top_left, top, top_right, left, center, right, bottom_left, bottom, bottom_right.
Combine with indent: to add a uniform inset.
TrackItem:
{
size: auto, 32;
bg:
Text(font: Medium; value: &value; justify: left; indent: 12, 4),
Image(source: trash_icon; fit: contain; anchor: right; indent: 10);
};
The trash icon sits flush-right inside the 32px-tall row, with a 10px inset. The text uses its own indent for the left padding. Both layers share the same bounds and claim different parts of it.
>When a @Bitmap is declared with tile: it's a sprite sheet. Pick a named frame with the > operator inside Image(source: ...).
@Bitmap icons:
{
path: ":res:icons.png";
tile: 24; /* each frame is 24x24 */
};
ToolbarPlay: { bg: Image(source: icons > play; fit: contain); };
ToolbarStop: { bg: Image(source: icons > stop; fit: contain); };
ToolbarMute: { bg: Image(source: icons > mute; fit: contain); };
For continuous value-driven animation across all frames (rotary knobs, dials, scrubbing), use ImageArray instead.
Layers in bg: stack bottom-to-top. Mix Image with Fill, Border, Text, Line, Gradient to compose richer visuals.
PromoQueenW:
{
inherit: PromoButton;
bg:
Fill(colour: sq_light; corner: 6), /* tile background */
Image(source: piece_wq; fit: contain); /* piece on top */
@State hover:
{
bg:
Fill(colour: white; corner: 6),
Image(source: piece_wq; fit: contain);
};
};
The Fill paints a rounded tile, the Image paints the chess piece on top of it. Hovering swaps the fill colour but keeps the piece.
AddFloatTo pin a fixed-size image at one of the nine alignment points of a parent (close-X button, corner badge, watermark, floating action button), reach for the layout side, not just the style side. The recipe:
size: plus bg: Image(...).AddFloat that child onto the parent at the alignment point you want.// In view.cpp
GLX::AddFloat(m_panel, m_close_btn, GLX::kAlignmentTopRight);
/* In styles.glx */
CloseButton:
{
size: 24, 24;
margin: 8;
bg: Image(source: close_icon; fit: contain);
};
The image renders into m_close_btn's 24x24 box. The box sits at the panel's top-right corner with an 8px margin. Swap kAlignmentTopRight for any of the nine alignment values to pin the icon to a different corner or edge.
The same pattern covers:
kAlignmentBottomRightkAlignmentBottomLeftkAlignmentCenterkAlignmentTopRightkAlignmentTopLeftThe carrier object is just a normal styled GLX::Object. It can receive hover states, click events, transitions, everything else.