Images
Use @pracht/image for responsive image markup, reserved layout space, and deployment-specific optimization loaders.
Install
pnpm add @pracht/image
# Only needed when you use the built-in Node optimization endpoint.
pnpm add sharp@pracht/image is split into a framework-agnostic component entry and a Node endpoint entry. Import the component from @pracht/image; import the optimization handler from @pracht/image/node.
Render an Image
import { Image } from "@pracht/image";
export function Component() {
return (
<Image
src="/banner.jpg"
alt="Pracht banner"
width={1200}
height={280}
sizes="(max-width: 1200px) 100vw, 1200px"
priority
/>
);
}The component renders plain <img> markup, so it works during SSR and SSG without adding client runtime. loading="lazy" and decoding="async" are the defaults. Use priority for above-the-fold images; it switches the image to eager loading and adds fetchpriority="high".
Always provide meaningful alt text, or alt="" for decorative images.
Reserve Layout Space
Images need either intrinsic dimensions or fill:
<Image src="/card.jpg" alt="Product preview" width={640} height={360} />For background-style images, use fill inside a positioned parent:
<div style={{ position: "relative", height: "18rem" }}>
<Image
src="/hero.jpg"
alt="Pracht docs hero"
fill
sizes="100vw"
style={{ objectFit: "cover" }}
/>
</div>fill images stretch with position: absolute; inset: 0. The parent controls the rendered size, so give the parent a stable height or aspect ratio.
Mount the Default Endpoint
The default loader points at /api/_pracht/image. Add an API route at that path to resize and encode same-origin source images with sharp:
import { createImageHandler } from "@pracht/image/node";
const imageHandler = createImageHandler({
localOrigin: process.env.PRACHT_ORIGIN,
});
export const GET = imageHandler;
export const HEAD = imageHandler;This endpoint works in pracht dev, adapter-node, and Node-compatible runtimes. Set localOrigin to the same trusted public URL used by nodeAdapter({ canonicalOrigin }); loopback origins work without configuration during local development. The endpoint returns cacheable, revalidated responses, varies on Accept, and negotiates modern output formats such as WebP.
Configure Loaders
Loaders turn { src, width, quality } into a URL. Configure one globally when your deployment platform should serve image variants:
import { cloudflareLoader, configureImage } from "@pracht/image";
configureImage({
loader: cloudflareLoader,
quality: 75,
});| Loader | Best For |
|---|---|
defaultLoader |
The /api/_pracht/image endpoint |
cloudflareLoader |
Cloudflare Image Resizing |
vercelLoader |
Vercel Image Optimization |
passthroughLoader |
Static hosts without an image service |
You can also pass a loader prop to a single <Image> when one image needs different handling.
Remote Images
The Node endpoint accepts same-origin URLs by default. Allow remote hosts explicitly:
import { createImageHandler } from "@pracht/image/node";
const imageHandler = createImageHandler({
localOrigin: process.env.PRACHT_ORIGIN,
remotePatterns: [
{ protocol: "https", hostname: "images.example.com", pathname: "/uploads" },
],
});
export const GET = imageHandler;
export const HEAD = imageHandler;Every redirect destination is checked before it is requested. Widths are also restricted to configured breakpoints, which keeps attackers from filling your cache with arbitrary image variants.
Platform Notes
| Target | Recommendation |
|---|---|
| Node | Set the same trusted origin on nodeAdapter({ canonicalOrigin }) and createImageHandler({ localOrigin }), then use the default loader |
| Cloudflare Workers | Use cloudflareLoader; sharp does not run in Workers |
| Vercel | Use vercelLoader and keep Vercel image sizes aligned with your Pracht breakpoints |
| Static hosting | Use passthroughLoader so images render without an optimization backend |
See the examples/basic gallery route for a complete endpoint plus component example.