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