
npm install sveltekit-image-optimize> 💡Not to be confused with img:enhance that handles image optimizations at build time, this is for the external images, or images from CMSs and other sources. this package is more like cloudflare's image optimizer. a proxy right in your sveltekit app that auto optimizes images.
This is a simple utility that helps you create an endpoint of your svelte app that optimizes your images, it uses sharp to optimize images.
I have been using something similar for my small to medium projects, and decided maybe others can benefit from this as well.
This library is similar to what next/image image optimizer does, basically a proxy that optimizes/resizes images on the fly, with support for caching.
- SvelteKit Image Optimizer
- Table of Contents
- Why would you use this?
- Installation
- Setup
- Using Components
- Image Component Props
- OptimizeOptions Object
- Picture Component Props
- Â Advanced Hook Configuration
- Client side Configuration
- Component Optimize options
- Caching
- Cache Adapters
- Memory Cache
- File System Cache
- S3 Cache Adapter
- Usage
- Make Your own Adapter
- Animated Images
- Future plans
- Contribution
- License
Svelte Kit Image Optimize provides a seamless solution for image optimization in your Svelte Kit project. You should consider this library when
- You need to serve optimized, properly sized images to reduce page load times and improve Core Web Vitals
- You want automatic format conversion to modern formats like WebP and AVIF based on browser support
- You require responsive images that automatically adapt to different screen sizes and device capabilities
- You need flexible caching strategies (memory, filesystem, or S3) to improve performance and reduce server load
- You want a simple, declarative API with Svelte components that handle the complexity of responsive images
- You need to optimize images from external domains with security controls
- You want to serve external images from your own domain, improving SEO.
> 💡 In many cased you do not have control over images, probably the content is created by writers or other non tech people, and you have no control over that, this can help you server optimized images without interfering with those content creators
To be able to use this package you need to install both sveltekit-image-optimize and sharp
> 💡 Note: If you already have sharp installed you can skip this.
``bash`
npm i sveltekit-image-optimize sharp
To begin using the optimizer all you have to do is add the image optimizer handler to your hooks.server.ts
hooks.server.ts:
`typescript filename="hooks.server.(ts/js)"
import { createImageOptimizer } from 'sveltekit-image-optimize';
const imageHandler = createImageOptimizer({
// optional params here
});
// add it to your hook handler (if you have multiple handlers use sequence)
export const handle: Handle = imageHandler;
`
> 💡 Note: while this simple example will get you going, I highly suggest you checkout the caching and the other options to optimize you setup
Then Any where in your project you can use the provided Image, Picture components or toOptimizedURL to generate an optimized image URL (useful for styles/background images and such)
`html
width="600"
height="420"
optimizeOptions={{
preload: "prefetch"
}}
/>
sources={[
{
media: '(min-width: 1024px)',
optimizeOptions: { width: 500, fit: 'cover', background: 'transparent'}
},
{
media: '(min-width: 768px)',
optimizeOptions: { width: 400, fit: 'cover', background: 'transparent'}
},
{
media: '(min-width: 480px)',
optimizeOptions: { width: 300, fit: 'cover', background: 'transparent'}
},
{
media: '(min-width: 320px)',
optimizeOptions: { width: 320, fit: 'cover', background: 'transparent'}
}
]}
/>
`
| Prop | Type | Description |
| ----------------- | -------- | ----------------------------------------------------------- |
| src | string | The source URL of the image (required) |alt
| | string | Alternative text for the image (required for accessibility) |optimizeOptions
| | object | Image optimization options (see below) |...rest
| | any | Any other HTML img attributes are passed through |
All these options are optional
| Option | Type | Description |
| ------------ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| width | number | Target width of the image, if the image already have width/height you do not have to provide these here |height
| | number | Target height of the image, if the image already have width/height you do not have to provide these here |quality
| | number | Image quality (1-100) |format
| | string | Output format (avif, webp, jpeg, png, etc.), recommended is to omit this option because the optimization endpoint will select the best format based on the accept header |fit
| | string | Resize behavior (cover, contain, fill, inside, outside) |position
| | string | Position for cropping (center, top, left, etc.) |background
| | string | Background color when using fit modes that need it |preload
| | 'preload' \| 'prefetch' or undefined | if provided a will be added to
|$3
For more details you can check the component source
| Prop | Type | Description |
| ------------------- | --------- | -------------------------------------------------------------- |
|
src | string | The source URL of the image (required) |
| alt | string | Alternative text for the image (required for accessibility) |
| defaultOptions | object | Default optimization options applied to all sources |
| sources | array | Custom responsive image sources (see below) |
| useDefaultSources | boolean | Whether to use built-in responsive breakpoints (default: true) |
| aspectRatio | number | Optional aspect ratio to maintain (e.g., 16/9) |
| ...rest | any | Any other HTML img attributes are passed through |Each source in the
sources array should have:| Property | Type | Description |
| ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
media | string | Media query (e.g., '(min-width: 768px)') |
| optimizeOptions | object | Image optimization options for this breakpoint, similar to image optimize options omitting the preload because it does not make sense here (we can't know which picture source the client is going to use hence we can't ask to prefetch it) |When
useDefaultSources is true, the Picture component includes sensible default responsive breakpoints from 540px to 5120px. Advanced Hook Configuration
createImageOptimizer takes an object of Options| Option | Type | Default | Description |
| ------------------ | ------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
route | string | /api/image-op | The endpoint path where the image optimizer will handle requests |
| formatPriority | Array | ['avif', 'webp', 'jpeg', 'jpg', 'png', 'gif'] | Priority order for image formats |
| fallbackFormat | string | jpeg | Default format to use when no format is specified |
| quality | number | 75 | Default image quality (1-100) |
| cache | ImageCacheHandler | undefined | Cache adapter for storing optimized images check Caching |
| cacheTTLStrategy | number \| 'source' \| 'immutable' \| 'no-cache' | 'source' | Cache TTL strategy, by default what ever cache policy is received from the source image is used. |
| allowedDomains | Array | undefined | Domains that are allowed to be optimized (security feature) |
| minSizeThreshold | number \| undefined | 5kb | The minimum size of the image to be optimized. Defaults to 5kb. |
| skipFormats | Array | avif, webp | The formats that will not be optimized, this only applies to images without a resize query. Defaults to avif, webp |Client side Configuration
If you've customized the default
route, you need to configure the client side as well in hooks.client.tshooks.client.ts:`typescript
import { initImageOptimizerClient } from 'sveltekit-image-optimize/client';
import type { ClientInit } from '@sveltejs/kit';export const init: ClientInit = () => {
// this is only needed if you change the default route /api/image-op
// iF you do not change that , you don't have to initialize it on client side
initImageOptimizerClient({
route: '/api/image-op-2'
});
};
`Component Optimize options
Caching
This library provides several cache adapters for storing optimized images:
Cache Adapters
This library provides several cache adapters for storing optimized images:
$3
Memory cache is suitable for development environments. Not recommended for production as there is no cleanup other than
ttl, so it could if you have many many images consume a lot of ram, and this is basically useless in serverless environments.`typescript
import { createMemoryCache } from 'sveltekit-image-optimize/cache-adapters';const cache = createMemoryCache();
`$3
File system cache stores images on disk. Suitable for single-server deployments, For self hosting (single VPS) this is probably the best way.
`typescript
import { createFileSystemCache } from 'sveltekit-image-optimize/cache-adapters';const cache = createFileSystemCache('/path/to/cache/directory');
`$3
The S3 cache adapter allows storing optimized images in Amazon S3 or S3-compatible storage. This is recommended for multi-server deployments or serverless environments.
To use the S3 cache adapter, you need to install the AWS SDK packages: These packages are omitted from dependencies on purpose as the usage of the adapter is optional
`bash
npm install @aws-sdk/client-s3
`#### Usage
`typescript
import { createS3Cache } from 'sveltekit-image-optimize/cache-adapters';const cache = createS3Cache({
region: 'us-east-1',
bucket: 'my-image-cache',
prefix: 'images', // optional, adds a prefix to all keys
// Auth credentials (optional, can use AWS environment variables)
accessKeyId: 'YOUR_ACCESS_KEY',
secretAccessKey: 'YOUR_SECRET_KEY',
// For S3-compatible services like MinIO, DigitalOcean Spaces, etc.
endpoint: 'https://custom-endpoint.com', // optional
forcePathStyle: true, // for minio or other providers, this only affect the genration of public links bucket.enpoint vs endpoint/bucket
/*
if true the cached images won't be proxied and piped through sveltekit, instead a redirect response will be sent to the client, useful to save bandwidth o sveltekit backend or to use s3 cdn etc...
if false a stream will be opened from s3 and piped straight through sveltekit response
when redirecting the server will redirect with a found status and give the same cache policy as if it proxied the request
*/
useRedirects: false
});
`Then use the cache with your image optimizer:
`typescript
import { imageOptimizer } from 'sveltekit-image-optimize';export const config = imageOptimizer({
cache: cache
// other options...
});
`#### Make Your own Adapter
Cache adapters have the same interface, all you have to do is implement your own logic.
`typescript
export interface ImageCacheHandler {
// if true and getPublicUrl returns a url a redirect response will be sent instead of sending the image content
useRedirects: boolean; // images are hashed based on url and the optimization parameter (with,height,format etc...)
getKey(hash: string): Promise<{
value: NodeJS.ReadableStream | Buffer | null;
ttl?: number;
}>;
// save this image to cache
setKey(
hash: string,
value: NodeJS.ReadableStream | Buffer | ReadableStream,
ttl?: number,
contentType?: string
): Promise;
// delete an image from cache
delKey(hash: string): Promise;
// if your adapter supports public urls you can handle that here
// useful with cdns and other caching platforms etc...
getPublicUrl(hash: string): Promise<{
url: string | null;
ttl?: number;
}>;
}
class MyAdapter implements ImageCacheHandler {
// implement thigs here
}
`Note that when implementing your own cache adapter, you are responsible for invalidating the cache based on the
ttl provided to you on setKey, the image optimizer handler will not keep records of ttl and rely of your adapter response.$3
Animated images will not be transformed as
sharp does not support them, they will be piped straight through.$3
- Add a fall back to use other libraries than
sharp` to add support for platforms what do not support sharp.Feel free to open an Issue or a pull request
MIT © 2025 humanshield85