A well-considered Svelte component for displaying images from Sanity. At a glance:
<img>
tag, no nested DOM structure to mess withimg
tag!srcSet
automatically based on the width
you specifysrcSet
factor based on image output widthwidth
and height
attributes accordinglycrop
and hotspot
values from the Sanity Studiohotspot
is providedNote: Low-quality image preview support is not yet implemented but is planned for a future release.
npm install sanity-svelte-image
# or
yarn add sanity-svelte-image
# or
pnpm add sanity-svelte-image
You can find the full writeup on getting going below, but in the interest of making it easy to see if this is the thing you are looking for, here's a quick example of most of what you'll need to know:
Simplest Case:
This will render the image out assuming it will be displayed at half its original width with a srcSet included (multiplies vary based on original image size):
<script lang="ts">
import { Image } from 'sanity-svelte-image';
let { image } = $props();
</script>
<Image
{image}
alt="Demo image"
/>
More full-featured example:
<script lang="ts">
import { Image } from 'sanity-svelte-image';
let { image, heroImage, thumbnailImage } = $props();
</script>
<!-- Hero image with priority loading -->
<Image
image={heroImage}
width={1200}
height={600}
mode="cover"
priority={true}
alt="Hero image"
class="w-full h-auto"
/>
<!-- Regular image with custom sizing -->
<Image
{image}
width={500}
height={250}
mode="cover"
queryParams={{ sharpen: 30, q: 80 }}
alt="Featured content"
class="rounded-lg shadow-md"
sizes="(min-width: 500px) 500px, 100vw"
/>
<!-- Natural size image (preserves original dimensions) -->
<Image
image={thumbnailImage}
natural={true}
alt="Thumbnail at natural size"
class="max-w-xs"
/>
That's the gist. Read on for more. π
How it works at a glance:
src
and srcSet
props generated based on the width
and height
props you pass in (or the image dimensions if you don't pass in a
width or height)srcSet
widths depend on the size of the output image and the original
image; there's logic to avoid wasteful tiny images or giant jumps in size
between large entriessrcSet
are never duplicated and never upscale the imagewidth
and height
attributes are set automatically to avoid layout shiftsauto=format
- Sanity will use AVIF images if they're supported by the
browser (note: if you specify fm
manually, this won't be set)fit
- if the image aspect ratio isn't changed, this will be set to max
;
if the aspect ratio will change it's set to crop
q
- the quality is set to 75 by default, but you can override it with the
queryParams
proploading
attribute will be set to lazy
if it isn't supplied; use
loading="eager"
for images above the fold or set priority={true}
alt
attribute will be set to an empty string if it isn't supplied; set
it if it isn't a decorative image!This component accepts all standard HTML img
attributes plus the following Sanity-specific props:
image
(SanityImageAsset) β Required - The Sanity image object containing the asset reference and optional crop/hotspot datamode
("cover" | "contain") β Optional - Use cover
to crop the image to
match the requested aspect ratio (based on width
and height
). Use
contain
to fit the image to the boundaries provided without altering the
aspect ratio. Defaults to "contain"
unless natural={true}
is set.width
(number) β Optional - The target width of the image in pixels. Only
used for determining the dimensions of the generated assets, not for layout.
Use CSS to specify how the browser should render the image instead.height
(number) β Optional - The target height of the image in pixels. Only
used for determining the dimensions of the generated assets, not for layout.
Use CSS to specify how the browser should render the image instead.natural
(boolean) β Optional - When true, uses the original image dimensions
and sets mode to "contain". Defaults to false
.priority
(boolean) β Optional - When true, sets loading="eager"
,
fetchpriority="high"
, and decoding="sync"
for above-the-fold images.
Defaults to false
.baseUrl
(string) β Optional - Custom base URL for the Sanity CDN. If not
provided, uses environment variables PUBLIC_SANITY_PROJECT_ID
and
PUBLIC_SANITY_DATASET
.queryParams
(object) β Optional - An object of query parameters to pass to
the Sanity Image API. See the
Sanity Image API documentation for a
list of available options.Since some props conflict with native <img>
attributes, these prefixed props
are provided:
htmlWidth
(number) β Override the computed width attributehtmlHeight
(number) β Override the computed height attribute htmlId
(string) β Set the id
attribute (since id
is used for Sanity image ID)Add your Sanity project configuration to your environment variables:
PUBLIC_SANITY_PROJECT_ID=your-project-id
PUBLIC_SANITY_DATASET=production
For SvelteKit, these should be in your .env
file and prefixed with PUBLIC_
to be available on the client side.
mode
If you are providing only one dimension (width
or height
, but not both), it
doesn't matter since the behavior will be the same.
priority
and natural
propsPriority images are for above-the-fold content that should load immediately:
<!-- Hero image that should load first -->
<Image
image={heroImage}
width={1200}
height={600}
priority={true}
alt="Page hero"
class="w-full h-auto"
/>
Natural sizing preserves the original image dimensions without any resizing:
<!-- Profile picture at its natural size -->
<Image
image={avatarImage}
natural={true}
alt="User avatar"
class="rounded-full max-w-20"
/>
<!-- Logo that should maintain its exact proportions -->
<Image
image={logoImage}
natural={true}
alt="Company logo"
class="h-12 w-auto"
/>
I recommend setting something like the following Tailwind classes for images in your project,
then overriding styles as needed. This will ensure images act like block-level
elements with infinitely scalable contents even with the width
and height
attributes set:
/* Add to your global CSS */
img {
@apply block max-w-full w-full h-auto;
}
Or apply directly to images:
<Image {image} class="block max-w-full w-full h-auto" alt="My image" />
Here's an example of how that works when using a 3-column grid:
<script lang="ts">
import { Image } from 'sanity-svelte-image';
let { images } = $props();
</script>
<div class="grid grid-cols-3 gap-4 max-w-screen-xl px-5 mx-auto">
{#each images as image (image._id)}
<div>
<Image
{image}
width={390}
sizes="(min-width: 1240px) 390px, calc((100vw - 40px - 30px) / 3)"
alt="Gallery image"
class="block max-w-full w-full h-auto"
/>
</div>
{/each}
</div>
If you need these images to all match in height, switch to cover
mode:
<Image
{image}
width={390}
height={260}
mode="cover"
sizes="(min-width: 1240px) 390px, calc((100vw - 40px - 30px) / 3)"
alt="Gallery image"
/>
Using the Image component for background images:
<script lang="ts">
import { Image } from 'sanity-svelte-image';
let { backgroundImage } = $props();
</script>
<section class="relative py-24">
<Image
image={backgroundImage}
width={1440}
class="absolute inset-0 w-full h-full object-cover select-none z-0"
alt=""
/>
<div class="relative z-10">
<h1 class="text-4xl font-bold">Your big hero copy</h1>
<a href="/signup/" class="inline-block mt-4 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Get started
</a>
</div>
</section>
If you're using Sanity's GROQ query language to fetch data, here is how I recommend fetching the image fields:
// For a simple image field
image {
...
}
// If you need the asset reference directly
image {
asset->{
_id,
_ref
},
hotspot { x, y },
crop {
bottom,
left,
right,
top,
}
}
The component exports the following TypeScript types:
import type {
SanityImage, // Main component props type
SanityImageAsset // Sanity image object type
} from 'sanity-svelte-image';
// You can also import the baseUrl helper
import { baseUrl } from 'sanity-svelte-image';
<picture>
element support with <source>
tag renderingMIT License - see LICENSE file for details.
Issues and pull requests are welcome! Please check the existing issues before creating a new one.
This component is inspired by and based on the excellent sanity-image React component by Corey Ward. The Svelte implementation provides the same powerful features adapted for the Svelte ecosystem.