Skip to main content

Overview

Spree handles uploads, processing, and delivery for product media. Images are automatically converted to WebP format and preprocessed into multiple sizes for optimal performance.

Product Media

A media record carries:
  • Position for ordering within the gallery
  • Media typeimage, video, or external_video (defaults to image)
  • Alt text for accessibility and SEO
  • Focal point coordinates for smart cropping
  • Preprocessed named variants for fast delivery
  • variant_ids — which product variants the media represents. An empty array means it represents the product as a whole.
In Spree 5.5 the product is the default owner of media. Before 5.5, every image was pinned to a specific variant (usually the master), and sharing the same image across variants meant re-uploading the file. From 5.5 onward, an image lives on the product, and any subset of variants can reference it through variant_ids — without duplicating the underlying file.

Uploading a product-level image

Creating media from a remote URL

When the image already lives at a public URL, pass url instead of a signed_id — Spree fetches the remote file and stores it as product media, so you skip the direct-upload step entirely.
The fetch runs in the background, so this request returns 202 Accepted with no body — the media appears in the gallery once the download and processing finish. Re-fetch the product’s media to know when it’s ready.

Sharing a single image across variants

Pass a variant_ids array on the same media endpoint to link/unlink variants. The server replaces the asset’s link set on every call — empty array clears all links, omitting the field leaves them untouched.
Variants belonging to a different product are silently dropped — the API rejects cross-product tampering at the model layer. Reordering happens once on the product gallery; every linked variant inherits the new order. The Store API’s media field on a product returns its gallery — product-level media when present, falling back to legacy variant-pinned images during the transition. On a variant, media returns the assets linked to that variant via variant_ids, falling back to direct variant uploads. This dual rendering means existing storefronts keep working during the upgrade; new uploads attach to the product, and you opt into a one-shot migration to re-home legacy variant-pinned data when convenient.

Video

A product gallery can hold video as well as images. Spree supports both ways merchants usually have it: Spree reads the link once, when it is saved, and rejects anything it cannot embed — so a broken URL is caught at the point a merchant enters it rather than in the storefront. What it derives comes back on the media object: Because the derived fields are on the response, a storefront embeds a video without parsing links itself.

Adding an external video

Uploading a video file

A hosted video is uploaded the same way an image is, with media_type telling Spree what it is. Add poster_signed_id to give it a still frame:
Admin SDK
A poster can also be added or replaced later, on its own:
Admin SDK
Spree serves an uploaded video as you uploaded it — it does not transcode. Keep files web-friendly (H.264 MP4 or WebM) so they play everywhere, and prefer an external video for long footage so the provider handles streaming.

Posters

A video has no image of its own, so its sized URLs (small_url, large_url, and the rest) resolve to its poster — the still shown before the video plays. A gallery that only knows how to draw an image still renders the right picture, and can play the video when the shopper asks for it. Where the poster comes from, in order:
  1. The one the merchant uploadedposter_signed_id on write, editable in the dashboard’s media editor.
  2. The provider’s own still, for a YouTube link.
  3. Nothing, for a Vimeo link or an uploaded file with no poster — the tile falls back to a placeholder.
Spree does not extract a frame from an uploaded video, so give hosted video and Vimeo links a poster if you want them to show a still.

Focal Point

focal_point_x and focal_point_y mark the part of an image that must stay in frame when a storefront crops it to a different shape. Both are fractions between 0 and 1, measured from the top left, so { x: 0.5, y: 0.5 } is dead centre — which is also what a storefront should assume when they are null.
Admin SDK
Spree stores the focal point and serves it; the cropping itself is the storefront’s decision, since only it knows the shape it needs.

Named Variant Sizes

When an image is uploaded, Spree automatically generates optimized versions in the background: All variants are cropped to fill the exact dimensions and converted to WebP format.

Store API

Thumbnails (Always Available)

Every product response includes a thumbnail_url field — ready to use without any expands. Similarly, each variant includes a thumbnail_url and a media_count counter.
Avoid using ?expand=media on listing pages. This loads all media for every product in the response. Use thumbnail_url instead and only expand full media on product detail pages.

Full Media (On Demand)

On the product detail page, expand media and variants to get the full set of media with all named variant URLs:
Response (media object):

Media Fields Summary

Media Object Fields

Image Processing

Spree uses libvips for image processing. Images are automatically:
  • Converted to WebP format for optimal file size
  • Preprocessed on upload into all named variant sizes
  • Cached for subsequent requests

Storage

Spree supports two storage service types:
For production deployments, use cloud storage (S3, GCS, Azure) instead of local disk storage. See Asset Deployment for configuration details.

Best Practices

  • Use thumbnail_url on listing pages — avoid loading full media via expand
  • Always provide alt text for accessibility and SEO
  • Use named variant sizes (mini, small, medium, large, xlarge) for optimal performance
  • Use a CDN in production for faster delivery
  • Give every video a poster so a gallery has something to show before playback