Image
An img that loads lazily by default and asks the Salla CDN for a resized copy, with optional srcset, aspect ratio and mobile source.
import { Image } from '@salla.sa/twilight-theme-engine/components/common';In plain words
A store page is full of pictures, and each one costs download time. Image renders a normal <img> tag that behaves well by default: the browser loads it only when the shopper scrolls near it, and for photos on Salla's CDN it asks for a copy at the size you display (width={300} downloads a 300-pixel-wide file, not the 2000-pixel original).
Give priority to the one large image at the top of a page, so it loads first instead of waiting.
Signature
const Image: MemoExoticComponent<(props: ImageProps) => JSX.Element | null>
// ImageProps is not exported. Every <img> attribute, plus:
interface ImageProps extends ImgHTMLAttributes<HTMLImageElement> {
src?: string; // empty: renders nothing
alt: string; // required
priority?: boolean; // default false
aspectRatio?: string; // '1/1', '16/9'…: adds a wrapper <div>
objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down'; // default 'cover'
mobileSrc?: string; // adds <picture> with a mobile <source>
mobileBreakpoint?: number; // default 768
noWrapper?: boolean; // default false
srcSetWidths?: readonly number[]; // CDN widths for srcset
}Try it live
Loading a product from the demo store…
import { Image } from '@salla.sa/twilight-theme-engine/components/common';
export function Photo({ src, alt }: { src?: string; alt: string }) {
return (
<div style={{ width: 240 }}>
<Image
src={src}
alt={alt}
className="h-full w-full"
width={240}
/>
</div>
);
}
Example
import { Image } from '@salla.sa/twilight-theme-engine/components/common';
import type { Product } from '@salla.sa/twilight-theme-engine/types';
export function ProductThumb({ product }: { product: Product }) {
return (
<Image
src={product.image?.url}
alt={product.image?.alt ?? product.name}
className="h-full w-full"
width={300}
srcSetWidths={[150, 300, 600]}
sizes="(max-width: 640px) 50vw, 300px"
aspectRatio="1/1"
/>
);
}
How it behaves
Loading:
loading="lazy"anddecoding="async"by default. Withpriority:loading="eager",fetchPriority="high"anddecoding="sync". Passing any of these props yourself overrides them.Resizing applies only to URLs on
cdn.salla.sa,cdn.salla.networkandcdn.files.salla.network:srcbecomeshttps://<host>/cdn-cgi/image/fit=scale-down,onerror=redirect,format=auto,width=W,height=H/<path>, with any transform already in the URL removed first.fit=scale-downnever enlarges an image.srcSetWidthswrites asrcsetof<url> <width>wcandidates; passsizestoo.srcthen carries a width only (no height): yourwidthif it is one of the candidates, otherwise the first. An explicitsrcSetprop wins over the generated one.mobileSrcwraps the image in<picture>with a<source media="(max-width: 768px)">(seemobileBreakpoint), resized the same way.It always adds the Tailwind class
object-<objectFit>(object-coverby default) to the<img>.It is memoised (
React.memo) and eager, so it needs no<Suspense>. The engine uses it for the header logo, product cards (srcSetWidthswithaspectRatio="1/1"), the product gallery and cart items.The URL helpers behind it are
getCdnImageUrlandgetCdnImageSrcSetfrom@salla.sa/twilight-theme-engine/utils/cdn-image, for places that need a URL rather than an element.
Gotchas
stylegoes to the wrapper<div>, which exists only withaspectRatioand withoutnoWrapper. Without the wrapper thestyleprop is dropped entirely: Image.tsx takes it out of the props and never passes it to<img>. Style the image withclassName, or wrap it yourself.aspectRatioshapes the wrapper, not the picture. The<img>keeps its own height and the wrapper (overflow-hidden) crops it, soobjectFithas nothing to fit. AddclassName="h-full w-full"so the image fills the box, as the example does.For images on any other host (your theme's
/assets/…, a third-party URL)width,heightandsrcSetWidthschange only the markup: the full-size file still downloads, and nosrcsetis written.An empty or missing
srcrenders nothing, not an empty box. Reserve the space around it yourself, or the layout jumps when the URL arrives.
Related
Builds URLs for your theme's own asset files and for Salla's asset CDN, optionally asking the CDN to resize an image.
ProductCardThe product tile of every grid: image, badge, name, price, rating, wishlist heart and a real Add to cart button, in five layouts.
LinkAn anchor that moves between store pages without reloading, adding the language (and, on localhost or the preview host, the store) to the path.
Source and docs
- Engine source:
packages/theme-engine/src/components/common/Image.tsx