💡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.
Svelte Kit Image Optimize provides a seamless solution for image optimization in your Svelte Kit project. You should consider this library when
💡 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.
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:
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)
<script>
import { Image,Picture ,toOptimizedURL} from 'sveltekit-image-optimize/components';
</script>
<!--
if width and heigth are [rovided the image will be resized to those dimentions
if preload is provided a <link as='image'> will be added to the <head>
-->
<Image
src="{URL to my image supports relative link like /images/image}"
width="600"
height="420"
optimizeOptions={{
preload: "prefetch"
}}
/>
<!--
you can use picture too, you can spcify your sources here
the image optimizer endpoint will serve the image type based on the `accept` headers but you can provide a format field in `optimizeOptions` to enforce a specific format
-->
<Picture
src="{URL to my image supports relative link like /images/image}"
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'}
}
]}
/>
<!-- you can also just get the optimizedLink and ad it to your img element or use it how ever you see fit -->
<img src={toOptimizedURL("URL to my image supports relative link like /images/imag")} alt="">
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 |
OptimizeOptions
ObjectAll 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 <link/> will be added to <head> |
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.
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<string> |
['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<string|RegExp> | Function |
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 |
If you've customized the default route
, you need to configure the client side as well in hooks.client.ts
hooks.client.ts
:
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'
});
};
This library provides several cache adapters for storing optimized images:
This library provides several cache adapters for storing optimized images:
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.
import { createMemoryCache } from 'sveltekit-image-optimize/cache-adapters';
const cache = createMemoryCache();
File system cache stores images on disk. Suitable for single-server deployments, For self hosting (single VPS) this is probably the best way.
import { createFileSystemCache } from 'sveltekit-image-optimize/cache-adapters';
const cache = createFileSystemCache('/path/to/cache/directory');
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
npm install @aws-sdk/client-s3
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:
import { imageOptimizer } from 'sveltekit-image-optimize';
export const config = imageOptimizer({
cache: cache
// other options...
});
Cache adapters have the same interface, all you have to do is implement your own logic.
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<void>;
// delete an image from cache
delKey(hash: string): Promise<void>;
// 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.
Animated images will not be transformed as sharp
does not support them, they will be piped straight through.
sharp
to add support for platforms what do not support sharp.Feel free to open an Issue or a pull request
MIT © 2025 humanshield85