Repository navigation
Image
🌐 This page in: English · Português
A bitmap in an explicitly sized slot. The size is a constructor argument and not a hint, because layout can never infer extent from a source it has not decoded — a slot that waits for the network is a page that reflows in front of the reader.
Image("/images/hero.jpg", 640, 360, label: "The team at the 2026 offsite")Written with the factory, like the rest of the vocabulary. The same call realizes on the web and on Photon; nothing in it names a target.
| Parameter | Type | Meaning |
|---|---|---|
source |
string |
Where the artwork is. A path the target can fetch. |
width |
float |
The slot's width, in density-independent pixels. |
height |
float |
The slot's height. |
fit |
ImageFit |
How the artwork fills the slot. Cover by default. |
label |
string |
What a screen reader says. Empty means decorative, which is the HTML rule and holds on both targets. |
Three more are set as properties, because they are the exception rather than the shape:
| Property | Type | Meaning |
|---|---|---|
DarkSource |
string? |
The artwork for DARK mode, when one image cannot serve both. |
CornerRadius |
CornerRadii |
Per-corner radius; the artwork is clipped to it. |
Fit |
ImageFit |
Also settable here, for a tree built without the factory. |
new Image("/brand/wordmark.svg", 180, 40)
{
DarkSource = "/brand/wordmark-white.svg",
CornerRadius = new CornerRadii(8),
Label = "", // decorative: the name beside it already says it
}ImageFit |
What it does |
|---|---|
Cover |
Center-crop fill. The default, and what a photograph wants. |
Contain |
Letterboxed inside the slot, whole source visible. |
Stretch |
Distorting fill. Reserved for art that tolerates it. |
A wordmark drawn in ink for light surfaces and in white for dark ones cannot be one file. Colors
solve this with a ColorToken pair; artwork cannot, so the pair lives on the node
and the realizer resolves it the same way — no branch in app code, and no mode question asked at
paint time.
On the web both sources ship and CSS shows one, following the OS preference and the app's own
forced mode, which the theme controller stamps as data-theme.
Web: an <img> with the slot's width and height, object-fit from Fit, border-radius from
CornerRadius, src from Source and alt from Label.
Photon: the engine draws a SurfaceSubtle placeholder box under the same radius until texture
upload lands — the documented degradation pattern, stated here so the shape of the page is the same
on both targets even while the pixels are not.
v1 fences, shared by both: nine-slice, the loading and error states, and the decode crossfade join with the asset and animation systems rather than arriving on this node.
eQuantic.UI.Images adds an endpoint: a standalone service that resizes, re-encodes and caches
images on demand.
Read this first, because the shape of the feature is not what it looks like. The endpoint is
not wired to the Image node. Nothing in the vocabulary asks it for a resized variant, and the node
has no quality, srcset or opt-in property — the page you are reading used to say otherwise, and it
described a web-only element that preceded the vocabulary. What ships is the service; pointing
something at it is the caller's move today.
builder.Services.AddUI(options =>
{
options.UseImageOptimization(opts =>
{
opts.DefaultQuality = 80; // 1-100, default 75
opts.Formats = ["image/webp"]; // preferred output formats
opts.CacheTtlSeconds = 14400; // default 4 hours
opts.MaxSourceSize = 10 * 1024 * 1024; // default 10 MB
});
});
app.UseStaticFiles();
app.MapUI(); // maps /_equantic/imageGET /_equantic/image?url=<path>&w=<width>&q=<quality>
| Parameter | Rule |
|---|---|
url |
Required. Must start with / and must not contain .., so it cannot leave the content root. |
w |
Required. Must be one of AllowedWidths, or the answer is 400 naming the allowed values. |
q |
Optional, 1–100. Falls back to DefaultQuality. |
The response format is negotiated from the request's Accept header against Formats, with a
fallback the browser can always read. A miss resizes, converts and writes the result to
CacheDirectory; a hit serves the cached bytes with Cache-Control: public, max-age=<CacheTtlSeconds>.
A source larger than MaxSourceSize is refused rather than decoded, whoever hands it over: the
optimizer and the placeholder generator stop reading at the budget, so a stream a caller opens is
held to it as a file under wwwroot is.
A source is read when its bytes are a JPEG, PNG, GIF, WebP or BMP, whatever its name says. A
.tiff is refused before it is opened, and a file the optimizer cannot read, or one whose header
asks for more than 16,383 × 16,383 pixels, is answered 400 with no pixel decoded. The optimizer
runs on SkiaSharp, and its natives for Linux, macOS and Windows come with the package, so there is
nothing to install and no license key to set.
-
Orientation. The orientation a camera recorded is applied to the pixels, so
wis the width as displayed, andImageOptimizer.GetDimensionsAsyncanswers the size as displayed. - Animation. An animated GIF or WebP is served as it is, with its own content type, as the Next.js optimizer serves one: no encoder here writes frames.
-
The response. It is one of the formats an encoder here writes, so
Formatstakesimage/webp,image/pngandimage/jpeg, and any other,image/avifincluded, stops the app at startup. The pixels are sRGB and carry no colour profile, which a browser reads as sRGB.
| Option | Default | Meaning |
|---|---|---|
DefaultQuality |
75 |
Quality when the request does not say. |
DeviceSizes |
640, 750, 828, 1080, 1200, 1920, 2048, 3840 |
Widths for full-width images. |
ImageSizes |
32, 48, 64, 96, 128, 256, 384 |
Widths for smaller-than-viewport images. |
AllowedWidths |
derived |
DeviceSizes and ImageSizes, deduplicated and sorted. The validation list. |
Formats |
image/webp |
Output formats, best first: image/webp, image/png, image/jpeg. |
CacheTtlSeconds |
14400 |
What the response tells the browser. |
CacheDirectory |
obj/eQuantic/image-cache |
Where the encoded results live. |
MaxSourceSize |
10 MB |
Above this a source is refused, read by the endpoint or handed over by a caller. |
BlurPlaceholderGenerator turns a stream or a file into a tiny base64 data URL. Resolved from the
services UseImageOptimization registers, or injected, it reads no source past the app's
MaxSourceSize:
var placeholders = app.Services.GetRequiredService<BlurPlaceholderGenerator>();
var placeholder = await placeholders.GenerateFromFileAsync("wwwroot/images/hero.jpg");It is a standalone helper for the same reason the endpoint is: the vocabulary has no placeholder property to hand the result to.
- Components — the rest of the vocabulary
-
Design System — tokens, including the
ColorTokenpairDarkSourcemirrors -
Styling —
HtmlElementand the web-only escape hatch
🌐 English · Português
🏁 Start here
🏗️ Architecture
- Architecture Overview
- Write-Once Components
- Declarative Surface
- Package Architecture
- Components
- Styling
- Localization
- Analytics & GTM
📱 Write-once
⚙️ Compilation
⚡ Runtime
🔌 Server
🎨 Ecosystem
🚀 Development