Images
The image() helper generates responsive <img> tags with srcset, sizes, lazy loading, and CLS-preventing dimensions.
# Basic usage
import { image } from "@deijose/nix-js-kit";
export default function HeroPage() {
return html`
${image({
src: "/images/hero.jpg",
alt: "Hero image",
width: 1920,
height: 1080,
})}
`;
}Generates:
<img
src="/images/hero.jpg"
alt="Hero image"
width="1920"
height="1080"
loading="lazy"
decoding="async"
/># Responsive srcset
image({
src: "/images/hero.jpg",
alt: "Hero",
width: 1920,
height: 1080,
widths: [640, 1280, 1920],
sizes: "100vw",
})Generates:
<img
src="/images/hero.jpg"
alt="Hero"
width="1920"
height="1080"
srcset="/images/hero-640w.jpg 640w, /images/hero-1280w.jpg 1280w, /images/hero-1920w.jpg 1920w"
sizes="100vw"
loading="lazy"
decoding="async"
/># Priority (above-the-fold)
image({
src: "/images/hero.jpg",
alt: "Hero",
width: 1920,
height: 1080,
priority: true,
})When priority: true:
loading="eager"(instead oflazy)fetchpriority="high"is added
# ImageOptions
| Option | Type | Default | Description |
|---|---|---|---|
src |
string |
— | Image source path (required) |
alt |
string |
— | Alt text (required) |
width |
number |
— | Intrinsic width (required) |
height |
number |
— | Intrinsic height (required) |
widths |
number[] |
— | Width variants for srcset |
sizes |
string |
— | Sizes attribute for srcset |
loading |
"lazy" | "eager" |
"lazy" |
Loading strategy |
decoding |
"async" | "sync" |
"async" |
Decoding hint |
priority |
boolean |
false |
Sets eager + fetchpriority=high |
# Build-time optimization with sharp
Install the optional peer dependency:
bun add sharpWhen sharp is available, build() automatically:
- Processes all images registered via
image() - Generates WebP and AVIF variants at each requested width
- Hashes file contents for indefinite caching
- Writes optimized files to the output directory
import { isSharpAvailable } from "@deijose/nix-js-kit";
if (isSharpAvailable()) {
console.log("sharp is installed — images will be optimized");
} else {
console.log("sharp not found — images will pass through unoptimized");
}# Build integration
import { build } from "@deijose/nix-js-kit";
const result = await build({
appDir: "./src/app",
outDir: "./dist",
imageFormats: ["webp", "avif"], // default: ["webp", "avif"]
});
console.log(`Processed ${result.imagesProcessed} images`);# Programmatic pipeline
import { processImages, consumeImageRegistry } from "@deijose/nix-js-kit";
const registry = consumeImageRegistry();
const processed = await processImages({
registry,
outDir: "./dist/images",
formats: ["webp", "avif"],
widths: [640, 1280, 1920],
});# getImage() — single image transform
Since v2.0.2, you can process a single image programmatically without going through the build registry:
import { getImage } from "@deijose/nix-js-kit/image";
const result = await getImage({
src: "/images/hero.jpg",
publicDir: "./public",
outDir: "./dist/images",
widths: [640, 1280, 1920],
formats: ["webp", "avif"],
});
// result.variants: [{ url, width, height, format, size }, ...]
// result.entry: { src, width, height, hash, variants }# ImageService — programmatic image API
createImageService() gives you a stateful image service with declared capabilities, useful for runtime transforms on serverless/edge hosts:
import { createImageService } from "@deijose/nix-js-kit/image";
const service = createImageService({
publicDir: "./public",
outDir: "./dist/images",
capabilities: { imageRuntime: true, filesystem: "persistent" },
});
const result = await service.transform({
src: "/images/hero.jpg",
widths: [640, 1280],
formats: ["webp"],
});# Manifest API
The image manifest maps source URLs to their generated variants. Use it to validate markup URLs or inspect what was built:
import {
readManifest,
writeManifest,
getManifestEntry,
buildSrcset,
buildPictureMarkup,
validateManifestUrls,
processImageBatch,
} from "@deijose/nix-js-kit/image";
// Read a previously written manifest
const manifest = await readManifest("./dist/images/manifest.json");
// Look up a specific image
const entry = getManifestEntry(manifest, "/images/hero.jpg");
// Build a srcset string from an entry
const srcset = buildSrcset(entry);
// Validate that every URL in your HTML exists in the manifest
validateManifestUrls(html, manifest);# Build hardening (v2.0.2+)
The image pipeline was hardened with several guarantees:
- SHA-256 transform keys — variant filenames incorporate a hash of: content digest + normalized transform options + encoder version + naming version. The same request always produces the same URL; different options produce different URLs.
- Path containment — source and output paths are validated against
publicDir/outDir. No traversal (../), NUL bytes, symlink escape, or Unicode separator tricks. - Atomic writes — variants are written to a temp file then renamed, so partial writes never appear in the output.
- Single-flight — concurrent requests for the same transform key share one processing job instead of duplicating work.
- Bounded concurrency — a configurable pool (default 4) limits simultaneous sharp transforms.
strictmode — when enabled, missing sources or failed transforms fail the build instead of silently emitting partial output.
import { processImages } from "@deijose/nix-js-kit";
await processImages({
registry,
outDir: "./dist/images",
strict: true, // fail on any error
concurrency: 8, // parallel sharp transforms
});strict: true is recommended for production builds. Without it, a missing source image produces a broken srcset entry that browsers silently skip.
# Subpath export
import { image } from "@deijose/nix-js-kit/image";