Image Service API
astro:assets
was designed to make it easy for any image optimization service to build a service on top of Astro.
What is an Image Service?
Section titled What is an Image Service? Astro provides two types of image services: Local and External.
- Local services handle image transformations directly at build for static sites, or at runtime both in development mode and SSR. These are often wrappers around libraries like Sharp, ImageMagick, or Squoosh. In dev mode and in SSR, local services use an API endpoint to do the transformation.
- External services point to URLs and can add support for services such as Cloudinary, Vercel, or any RIAPI-compliant server.
Building using the Image Services API
Section titled Building using the Image Services API Service definitions take the shape of an exported default object with various required methods (“hooks”).
External services provide a getURL()
that points to the src
of the output <img>
tag.
Local services provide a transform()
method to perform transformations on your image, and getURL()
and parseURL()
methods to use an endpoint for dev mode and SSR.
Both types of services can provide getHTMLAttributes()
to determine the other attributes of the output <img>
and validateOptions()
to validate and augment the passed options.
External Services
Section titled External Services
An external service points to a remote URL to be used as the src
attribute of the final <img>
tag. This remote URL is responsible for downloading, transforming, and returning the image.
import type { ExternalImageService, ImageTransform, AstroConfig } from "astro";
const service: ExternalImageService = { validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) { const serviceConfig = imageConfig.service.config;
// Enforce the user set max width. if (options.width > serviceConfig.maxWidth) { console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`); options.width = serviceConfig.maxWidth; }
return options; }, getURL(options, imageConfig) { return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`; }, getHTMLAttributes(options, imageConfig) { const { src, format, quality, ...attributes } = options; return { ...attributes, loading: options.loading ?? 'lazy', decoding: options.decoding ?? 'async', }; }};
export default service;
Local Services
Section titled Local Services
To create your own local service, you can point to the built-in endpoint (/_image
), or you can additionally create your own endpoint that can call the service’s methods.
import type { LocalImageService, AstroConfig } from "astro";
const service: LocalImageService = { getURL(options: ImageTransform, imageConfig: AstroConfig['image']) { const searchParams = new URLSearchParams(); searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src); options.width && searchParams.append('w', options.width.toString()); options.height && searchParams.append('h', options.height.toString()); options.quality && searchParams.append('q', options.quality.toString()); options.format && searchParams.append('f', options.format); return `/my_custom_endpoint_that_transforms_images?${searchParams}`; // Or use the built-in endpoint, which will call your parseURL and transform functions: // return `/_image?${searchParams}`; }, parseURL(url: URL, imageConfig) { return { src: params.get('href')!, width: params.has('w') ? parseInt(params.get('w')!) : undefined, height: params.has('h') ? parseInt(params.get('h')!) : undefined, format: params.get('f'), quality: params.get('q'), }; }, transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig): { data: Uint8Array, format: OutputFormat } { const { buffer } = mySuperLibraryThatEncodesImages(options); return { data: buffer, format: options.format, }; }, getHTMLAttributes(options, imageConfig) { let targetWidth = options.width; let targetHeight = options.height; if (typeof options.src === "object") { const aspectRatio = options.src.width / options.src.height;
if (targetHeight && !targetWidth) { targetWidth = Math.round(targetHeight * aspectRatio); } else if (targetWidth && !targetHeight) { targetHeight = Math.round(targetWidth / aspectRatio); } }
const { src, width, height, format, quality, ...attributes } = options;
return { ...attributes, width: targetWidth, height: targetHeight, loading: attributes.loading ?? 'lazy', decoding: attributes.decoding ?? 'async', }; }, propertiesToHash: ['src', 'width', 'height', 'format', 'quality'],};export default service;
At build time for static sites and pre-rendered routes, both <Image />
and getImage(options)
call the transform()
function. They pass options either through component attributes or an options
argument, respectively. The transformed images will be built to a dist/_astro
folder. Their file names will contain a hash of the properties passed to propertiesToHash
. This property is optional and will default to ['src', 'width', 'height', 'format', 'quality']
. If your custom image service has more options that change the generated images, add these to the array.
In dev mode and SSR mode, Astro doesn’t know ahead of time which images need to be optimized. Astro uses a GET endpoint (by default, /_image
) to process the images at runtime. <Image />
and getImage()
pass their options to getURL()
, which will return the endpoint URL. Then, the endpoint calls parseURL()
and passes the resulting properties to transform()
.
getConfiguredImageService & imageConfig
Section titled getConfiguredImageService & imageConfig
If you implement your own endpoint as an Astro endpoint, you can use getConfiguredImageService
and imageConfig
to call your service’s parseURL
and transform
methods and provide the image config.
To access the image service config (image.service.config
), you can use imageConfig.service.config
.
src/api/my_custom_endpoint_that_transforms_images.ts
import type { APIRoute } from "astro";import { getConfiguredImageService, imageConfig } from 'astro:assets';
export const GET: APIRoute = async ({ request }) => { const imageService = await getConfiguredImageService();
const imageTransform = imageService.parseURL(new URL(request.url), imageConfig); // ... fetch the image from imageTransform.src and store it in inputBuffer const { data, format } = await imageService.transform(inputBuffer, imageTransform, imageConfig); return new Response(data, { status: 200, headers: { 'Content-Type': mime.getType(format) || '' } } );}
See the built-in endpoint for a full example.
Hooks
getURL()
Section titled getURL() Required for local and external services
getURL(options: ImageTransform, imageConfig: AstroConfig['image']): string
For local services, this hook returns the URL of the endpoint that generates your image (in SSR and dev mode). It is unused during build. The local endpoint that getURL()
points to may call both parseURL()
and transform()
.
For external services, this hook returns the final URL of the image.
For both types of services, options
are the properties passed by the user as attributes of the <Image />
component or as options to getImage()
. They are of the following type:
export type ImageTransform = { // ESM imported images | remote/public image paths src: ImageMetadata | string; width?: number; height?: number; widths?: number[] | undefined; densities?: (number | `${number}x`)[] | undefined; quality?: ImageQuality; format?: OutputFormat; alt?: string; [key: string]: any;};
parseURL()
Section titled parseURL() Required for local services; unavailable for external services
parseURL(url: URL, imageConfig: AstroConfig['image']): { src: string, [key: string]: any}
This hook parses the generated URLs by getURL()
back into an object with the different properties to be used by transform
(in SSR and dev mode). It is unused during build.
transform()
Section titled transform() Required for local services only; unavailable for external services
transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig: AstroConfig['image']): { data: Uint8Array, format: OutputFormat }
This hook transforms and returns the image and is called during the build to create the final asset files.
You must return a format
to ensure that the proper MIME type is served to users in SSR and development mode.
getHTMLAttributes()
Section titled getHTMLAttributes() Optional for both local and external services
getHTMLAttributes(options: ImageTransform, imageConfig: AstroConfig['image']): Record<string, any>
This hook returns all additional attributes used to render the image as HTML, based on the parameters passed by the user (options
).
getSrcSet()
Added in:
astro@3.3.0
Optional for both local and external services.
getSrcSet?: (options: ImageTransform, imageConfig: AstroConfig['image']): SrcSetValue[] | Promise<SrcSetValue[]>;
This hook generates multiple variants of the specified image, for example, to generate a srcset
attribute on an <img>
or <picture>
’s source
.
This hook returns an array of objects with the following properties:
export type SrcSetValue = { transform: ImageTransform; descriptor?: string; attributes?: Record<string, any>;};
validateOptions()
Section titled validateOptions() Optional for both local and external services
validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']): ImageTransform
This hook allows you to validate and augment the options passed by the user. This is useful for setting default options, or telling the user that a parameter is required.
See how validateOptions()
is used in Astro built-in services.
User configuration
Section titled User configuration
Configure the image service to use in astro.config.mjs
. The config takes the following form:
astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({ image: { service: { entrypoint: "your-entrypoint", // 'astro/assets/services/squoosh' | 'astro/assets/services/sharp' | string, config: { // ... service-specific config. Optional. } } },});
Reference