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
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.