Image ComponentThe Image component is a high-performance alternative to the standard HTML <img> tag, heavily inspired by Next.js. It helps optimize images for speed and search engine ranking.
•
Lazy Loading: Images are only loaded when they enter the viewport by default.
•
CLS Prevention: Automatically sets width and height to prevent layout shifts.
•
Layout Modes: Supports standard fixed sizing or a Fill mode for responsive containers.
•
Blur-up Placeholders: Support for low-resolution placeholders while the full image loads.
•
LCP Optimization: Use the Priority flag for critical above-the-fold images.
•
Server-Side Optimization: Auto resize, format conversion (WebP/AVIF), and quality control via eQuantic.UI.Images.
Src = "/path/to/image.jpg",
Requires Width and Height. The image maintains these dimensions.
The image will fill its parent container. The parent MUST have relative positioning.
ClassName = "relative h-64",
new Image { Src = "/hero.webp", Fill = true }
Use Blur placeholders for a smoother loading experience.
Placeholder = ImagePlaceholder.Blur,
BlurDataURL = "data:image/webp;base64,..."
The eQuantic.UI.Images package enables Next.js-style server-side image optimization. When enabled, local images are automatically resized, converted to WebP, and served via an optimized endpoint.
Image Component (Optimize = true)
/_equantic/image?url=/images/hero.jpg&w=640&q=80
▼ Image Optimization Endpoint
├── Validates parameters (width in allowed sizes, quality 1-100)
├── Checks filesystem cache (obj/eQuantic/image-cache/)
├── On miss: resize → format convert → cache → serve
└── Content negotiation: Accept header → WebP or JPEG fallback
1. Install the package
<PackageReference Include="eQuantic.UI.Images" />
2. Register services
builder.Services.AddUI(options =>
options.UseImageOptimization(opts =>
opts.DefaultQuality = 80; // 1-100, default: 75
opts.Formats = ["image/webp"]; // Preferred output formats
opts.CacheTtlSeconds = 14400; // Cache TTL (default: 4 hours)
opts.MaxSourceSize = 10 * 1024 * 1024; // Max source size (default: 10MB)
3. Map the endpoint
app.MapUI(); // Automatically maps /_equantic/image endpoint via UIOptions
Fixed-Size Images (1x/2x Density Descriptors)
When Width is set and Fill is false, the component generates 1x and 2x density descriptors for retina displays:
Src = "/images/hero.jpg",
Generated HTML:
src="/_equantic/image?url=%2Fimages%2Fhero.jpg&w=828&q=80"
/_equantic/image?url=%2Fimages%2Fhero.jpg&w=828&q=80 1x,
/_equantic/image?url=%2Fimages%2Fhero.jpg&w=1920&q=80 2x
Responsive Images (Width Descriptors)
When using Fill mode or without Width, the component generates a full srcset with all configured sizes:
Src = "/images/hero.jpg",
Generated HTML:
src="/_equantic/image?url=%2Fimages%2Fhero.jpg&w=3840&q=80"
srcset="/_equantic/image?url=%2Fimages%2Fhero.jpg&w=32&q=80 32w,
/_equantic/image?url=%2Fimages%2Fhero.jpg&w=48&q=80 48w,
... (all configured sizes) ...
/_equantic/image?url=%2Fimages%2Fhero.jpg&w=3840&q=80 3840w"
Override the global quality setting per image:
Src = "/images/background.jpg",
Quality = 50 // Lower quality for background images
Optimization is controlled at two levels:
Global (UseImageOptimization())
Per-Image (Optimize)
Result
Enabled
null (default)
Optimized
Enabled
false
Not optimized
Not called
null (default)
Not optimized
Not called
true
Optimized\*
\*Requires the endpoint to be mapped; otherwise, URLs will be generated but won't resolve.
// Explicit opt-out (e.g., SVGs don't need optimization)
Src = "/images/logo.svg",
•
Only local images (paths starting with /) are optimized. External URLs are never proxied.
•
Path traversal attacks (..) are blocked.
•
Width must be in the configured allowed sizes (DeviceSizes + ImageSizes).
•
Source file size is limited by MaxSourceSize (default 10MB).
The endpoint reads the browser's Accept header and serves the best supported format:
1.
Checks configured Formats (e.g., ["image/avif", "image/webp"])
2.
Returns the first format the browser supports
3.
Falls back to JPEG if no match
•
Filesystem cache: Optimized images are cached in obj/eQuantic/image-cache/ (configurable)
•
Cache key: SHA256 of (url, width, quality, format)
•
TTL: Configurable via CacheTtlSeconds (default: 4 hours)
•
Thread-safe: Concurrent requests for the same image are deduplicated
•
Response headers: Cache-Control: public, max-age={ttl}, Vary: Accept
Blur Placeholder Generator
The BlurPlaceholderGenerator service creates tiny 8px-wide JPEG placeholders as base64 data URLs:
var generator = app.Services.GetRequiredService<BlurPlaceholderGenerator>();
var dataUrl = await generator.GenerateFromFileAsync("wwwroot/images/hero.jpg");
// Returns: "data:image/jpeg;base64,/9j/4AAQ..."
Property
Type
Default
Description
DeviceSizes
int[]
[640, 750, 828, 1080, 1200, 1920, 2048, 3840]
Allowed widths for viewport-sized images.
ImageSizes
int[]
[32, 48, 64, 96, 128, 256, 384]
Allowed widths for smaller images.
Formats
string[]
["image/webp"]
Preferred output formats (ordered by priority).
DefaultQuality
int
75
Default quality (1-100).
CacheTtlSeconds
int
14400
Cache TTL in seconds (4 hours).
CacheDirectory
string
"obj/eQuantic/image-cache"
Filesystem cache directory.
MaxSourceSize
long
10485760
Max source image size in bytes (10MB).
Property
Type
Default
Description
Src
string
-
Image source URL.
Alt
string
-
Alternative text for accessibility.
Width
int?
-
Width in pixels.
Height
int?
-
Height in pixels.
Fill
bool
false
If true, fills the parent container.
Loading
ImageLoading
Lazy
Lazy or Eager.
Priority
bool
false
High priority for preloading (LCP).
Placeholder
ImagePlaceholder
None
Blur or Empty.
BlurDataURL
string?
-
Small Data URL for blurred background.
ObjectFit
string?
"cover"
CSS object-fit property (for Fill).
Optimize
bool?
null
Override global optimization setting.
Quality
int
0
Image quality (1-100). 0 = use global default.
SrcSet
string?
-
Manual srcset (ignored when optimized).
Sizes
string?
-
Manual sizes attribute.
The eQuantic.UI.Images package follows the self-contained package pattern:
└── ImageOptimizationState (static bridge)
eQuantic.UI.Components eQuantic.UI.Images
└── Image component ├── ImageOptimizer (SixLabors.ImageSharp)
├── ImageCache (filesystem + SemaphoreSlim)
├── ImageOptimizationMiddleware
├── BlurPlaceholderGenerator
└── ImageExtensions (DI + endpoint)
This avoids circular dependencies: Core defines the state, Components reads it, Images writes it.