Using vite-imagetools with SvelteKit

When building a web application, you may often need to work with images. While images are a great way to add visual appeal to your website, they can also make it slower and less performant. This is especially true when dealing with large images, which can take a long time to load.

Fortunately, there is a library called vite-imagetools that can help you optimize your images and make them load faster. In this tutorial, we'll show you how to use vite-imagetools in a SvelteKit project to improve your website's performance.

Install vite-imagetools

The first step to using vite-imagetools in your SvelteKit project is to install it. You can do this by running the following command in your project's root directory:

$ npm install vite-imagetools --save-dev

This will install vite-imagetools as a development dependency in your project.

Once vite-imagetools is installed, you can use it to optimize your images and make them load faster. In the next section, we'll show you how to configure vite.config.js to use vite-imagetools.

Updating vite.config.js

Now that vite-imagetools is installed, we need to configure vite.config.js to use it. Here's an example of how to update vite.config.js to use vite-imagetools:

import { defineConfig } from 'vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { imagetools } from 'vite-imagetools'; // Which image types should be converted const supportedExtensions = ['png', 'jpg', 'jpeg']; export default defineConfig(() => { const config = { plugins: [ // Run imagetools before sveltekit imagetools({ defaultDirectives: (url) => { const extension = url.pathname.substring(url.pathname.lastIndexOf('.') + 1); if (supportedExtensions.includes(extension)) { return new URLSearchParams({ // In addition to the original format also generate a WebP version format: 'webp;' + extension, // picture: true means imagetools will generate data suited for the picture element as: 'picture', }); } return new URLSearchParams(); }, }), // Run sveltekit after imagetools sveltekit(), ], }; return config; });

In this example, we're importing imagetools from vite-imagetools and using it as a plugin in the plugins array before the sveltekit plugin. We're also defining supportedExtensions, which is an array of image types that we want to convert using vite-imagetools.

We're using defaultDirectives to generate a WebP version of the image in addition to the original format. We're also setting picture: true to generate data suited for the element.

Add the imagetools type in your app.d.ts

To make TypeScript happy, we can follow benblazaks workaround. We need to add a type definition for vite-imagetools. We can do this by creating a app.d.ts file in the root of our project and adding the following code:

declare module '*&imagetools' { import { Picture } from 'vite-imagetools'; /** * actual types * - code https://github.com/JonasKruckenberg/imagetools/blob/main/packages/core/src/output-formats.ts * - docs https://github.com/JonasKruckenberg/imagetools/blob/main/docs/guide/getting-started.md#metadata */ const out: Picture; export default out; }

This code defines a TypeScript module declaration for *&imagetools. All we have to do is add &imagetools to the end of any image import path. This module declaration then tells the TypeScript compiler that vite-imagetools has a default export that is of an unknown type. We are essentially telling TypeScript to trust us that vite-imagetools will export a valid type that we can use later.

With this type declaration in place, TypeScript will no longer complain about missing types when we use vite-imagetools in our project.

Create an Image component using the picture tag

Now that we have vite-imagetools installed and configured, we can create an Image.svelte component that uses the picture tag to load our images. This component will uses srcset to automatically use the WebP versions of our images when the browser supports it, falling back to the original image format otherwise.

Here's an example implementation of the Image.svelte component:

Script

import type { Picture } from 'vite-imagetools'; export let src: Picture; export let alt: string; export let aspectRatio: string; export let sizes: string; export let background = 'transparent'; export let loading: 'eager' | 'lazy' = 'eager';

HTML

<picture style="width: 100%; aspect-ratio:{aspectRatio}; background-color: {background}"> {#each Object.entries(src.sources) as [format, srcset]} <source {srcset} {sizes} type="image/{format}" /> {/each} <img style="width: 100%; aspect-ratio:{aspectRatio}" src={src.img.src} {alt} {loading} /> </picture>

CSS

picture { display: flex; } img { width: 100%; }

This component defines an interface for an Image object that takes a img fallback source and an object of sources where each key is an image format and the value is an array of image sources for that format at different widths.

Inside the picture tag, we loop over the sources object and create a source tag for each format, setting the srcset attribute to a comma-separated list of the different image sources for that format at different widths. The type attribute is set to the MIME type of the format.

Finally, we add an img tag with the fallback source and any additional attributes like alt, sizes aspectRatio, loading and sizes.

With this component in place, we can use it to load our images throughout our SvelteKit project.

Using the Image.svelte component

After setting up the Vite-imagetools library in your SvelteKit project, you can now use the Image.svelte component to display optimized images on your website.

Here's an example of how to use the Image.svelte component:

Script

Import the Image.svelte component and your image file into your Svelte component or page:

import Image from '$lib/components/Image.svelte'; import myCoolImage from '../images/my-cool-image.png?w=800;600;275&imagetools'; const imageSizes = ` (min-width: 1024px) 800px, (min-width: 740px) 600px, 275px `;

In this example we're importing the image from the images directory and using the imagetools plugin to generate the different image sizes.

In the import we add a query string with the w parameter to specify the different image widths we want to generate. Here you can specify a single width or a comma-separated list of widths. This example uses three different widths: 800px, 600px and 275px in different viewports. However you could also specify double (and tripple) the width for high-density displays like retina screens. Thanks to the srcset attribute, the browser will automatically load the correct image for the screen density.

We also add the &imagetools at the end of the import so that the import matches the module declaration we previously added to our app.d.ts file to get TypeScript to stop complaining.

imageSizes is the set of sizes that the image can have based on the screen width. To read more about how srcset and sizes work, check out this post by Eric Portis.

HTML

Add the Image component to your HTML and pass in the imported image and sizes:

<div class="image-container"> <Image src={myCoolImage} sizes={imageSizes} background="#202b3f" aspectRatio="16 / 9" alt="My cool image" /> </div>

In this example, the src prop is set to the imported myCoolImage, the sizes prop is set to imageSizes.

The background prop could be set either to a neutral color or to the dominant color of the image. In this example, the color is set to #202b3f, which is the dominant color of the image.

The aspectRatio prop is set to 16 / 9, which is the aspect ratio of the image. This is used to set the aspect-ratio CSS property on the image container, which is used to reserve the height relative to the width of the image before it is loaded. This prevents the page from jumping around as the image loads.

The alt prop is set to My cool image, which is the alt text for the image.

CSS

Style the container to limit the maximum size of the image:

.image-container { width: 100%; max-width: 800px; } @media (min-width: 740px) { .image-container { width: 600px; } } @media (min-width: 1024px) { .image-container { width: 800px; } }

In this example, the .image-container class limits the maximum width of the image to 800px and adjusts the width based on the screen size.

You can of course use any other CSS properties to style the image container, and make it responsive however you like. The important thing is that the width and max-width properties are set to the same value as the sizes prop on the Image.

And that's it! You can now use the Image.svelte component to display optimized images in your SvelteKit project.

Thanks for checking out the site! Feel free to use any and all parts of the code available at github ♥ Oscar.