Sveltekit Image Optimize

v0.0.10

Simple utility that adds capabilities similar to `next/image` image optimizer to SvelteKit websites.

SvelteKitImage OptimizationFrontendPerformanceJavaScript

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.

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.

Why would you use this?

Svelte Kit Image Optimize provides a seamless solution for image optimization in your Svelte Kit project. 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.

Installation

To be able to use this package you need to install both sveltekit-image-optimize and sharp

npm
npm i sveltekit-image-optimize sharp
yarn
yarn add sveltekit-image-optimize sharp
pnpm
pnpm add sveltekit-image-optimize sharp

Setup

To begin using the optimizer all you have to do is add the image optimizer handler to your Server hooks.

hooks.server.ts typescript logo
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;

Using Components

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)

MyComponent.svelte html logo
<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="">

Image Component Props

PropTypeDescription
srcstringThe source URL of the image (required)
altstringAlternative text for the image (required for accessibility)
optimizeOptionsobjectImage optimization options (see below)
...restanyAny other HTML img attributes are passed through

OptimizeOptions Object

All these options are optional

OptionTypeDescription
widthnumberTarget width of the image, if the image already have width/height you do not have to provide these here
heightnumberTarget height of the image, if the image already have width/height you do not have to provide these here
qualitynumberImage quality (1-100)
formatstringOutput 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
fitstringResize behavior (cover, contain, fill, inside, outside)
positionstringPosition for cropping (center, top, left, etc.)
backgroundstringBackground color when using fit modes that need it
preload'preload' \| 'prefetch' or undefinedif provided a <link/> will be added to <head>

Picture Component Props

PropTypeDescription
srcstringThe source URL of the image (required)
altstringAlternative text for the image (required for accessibility)
defaultOptionsobjectDefault optimization options applied to all sources
sourcesarrayCustom responsive image sources (see below)
useDefaultSourcesbooleanWhether to use built-in responsive breakpoints (default: true)
aspectRationumberOptional aspect ratio to maintain (e.g., 16/9)
...restanyAny other HTML img attributes are passed through

Each source in the sources array should have:

PropertyTypeDescription
mediastringMedia query (e.g., '(min-width: 768px)')
optimizeOptionsobjectImage 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

OptionTypeDefaultDescription
routestring/api/image-opThe endpoint path where the image optimizer will handle requests
formatPriorityArray<string>['avif', 'webp', 'jpeg', 'jpg', 'png', 'gif']Priority order for image formats
fallbackFormatstringjpegDefault format to use when no format is specified
qualitynumber75Default image quality (1-100)
cacheImageCacheHandlerundefinedCache adapter for storing optimized images check Caching
cacheTTLStrategynumber \| 'source' \| 'immutable' \| 'no-cache''source'Cache TTL strategy, by default what ever cache policy is received from the source image is used.
allowedDomainsArray<string\|RegExp> \| FunctionundefinedDomains that are allowed to be optimized (security feature)
minSizeThresholdnumber \| undefined5kbThe minimum size of the image to be optimized. Defaults to 5kb.
skipFormatsArrayavif, webpThe 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.ts

hooks.client.ts typescript logo
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'
  });
};

Caching

This library provides several cache adapters for storing optimized images:

Cache Adapters

This library provides several cache adapters for storing optimized images:

Memory Cache

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 logo
import { createMemoryCache } from 'sveltekit-image-optimize/cache-adapters';

const cache = createMemoryCache();

File System Cache

File system cache stores images on disk. Suitable for single-server deployments, For self hosting (single VPS) this is probably the best way.

typescript logo
import { createFileSystemCache } from 'sveltekit-image-optimize/cache-adapters';

const cache = createFileSystemCache('/path/to/cache/directory');

S3 Cache Adapter

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
 npm install @aws-sdk/client-s3
yarn
 yarn add @aws-sdk/client-s3
pnpm
 pnpm add @aws-sdk/client-s3

Usage

s3-cache.ts typescript logo
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:

hooks.server.ts typescript logo
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.

custom-cache-adapter.ts typescript logo
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

Animated images will not be transformed as sharp does not support them, they will be piped straight through.

Future plans

  • Add a fall back to use other libraries than sharp to add support for platforms what do not support sharp.
  • Add some tests in place
  • based on feedback adjust the interface if needed
  • Add cropping / filter and other basic image manipulations?
  • Make the transformation queries compatible with unpic and deprecate this library’s components in favor of unpic’s components

Contribution

Feel free to open an Issue or a pull request

License

MIT © 2026 humanshield85