A file's other sizes and formats, by preset: a photograph's thumbnail,
its sizes for a srcset, its WebP. Only the presets' sizes are ever
made, so no one can have the server render at a size they choose.
config :storehouse, Storehouse.Variants,
presets: [
thumb: [width: 320, height: 320, fit: :cover],
photo: [widths: [640, 960, 1280, 1920, 2560], format: [:webp, :original]]
]
Storehouse.variant_url(file, :thumb)
#=> "/variants/2026/10/0b6…/harbour.jpg/thumb-320-5f3a91c2"
Storehouse.variant_url(file, :photo, width: 1000)
#=> "/variants/2026/10/0b6…/harbour.jpg/photo-1280-e04b77d1"What renders them is a Storehouse.Renderer: Storehouse.Renderer.Vix
(libvips, in the app; the default when Vix is loaded), which stores each
variant beside its original once, served by Storehouse.Variants.Plug;
or Storehouse.Renderer.Imgproxy, whose URLs an imgproxy behind a CDN
renders; or the app's own. See the Variants guide.
Configuration
:presets- a keyword list of presets, by name (an atom of lowercase letters, digits and_). See "Presets".:renderer- a module ofStorehouse.Renderer, or{module, options}. The default isStorehouse.Renderer.Vix, when Vix is loaded.:url- whereStorehouse.Variants.Plugserves variants, for a renderer without URLs of its own. The default is"/variants".:prefix- the prefix of the stored variants' keys:"<prefix><original key>/<preset>-<width>-<fingerprint>.<extension>", so deleting an original deletes its variants with one prefix. The default is"variants/".:timeout- how long a caller waits for a variant another is making, in milliseconds. The default is 60 seconds.
Each can be given to a call, for an app with variants of several kinds.
Presets
:width- the width, in pixels; or:widths, several, a ladder for asrcset. A width past the original's gives the original's: a variant is never enlarged.:height- the height of the box, with:width; or:aspect,{width, height}, the box's proportions at every width. The default is any height.:fit-:inside(the default: scaled to fit the box) or:cover(cropped to fill it, which needs a height).:focus- what a crop keeps::centre(the default),:attention,:entropyor{x, y}, a point in fractions from the top left.:format-:original(the default: the original's, or JPEG for a format browsers do not show),:webp,:jpeg,:png,:avif,:gif; or a list, in order, of which the first the browser takes is given, by itsAccept, the last to every browser:[:webp, :original].:quality- 1 to 100. The default is the renderer's.:profile-:srgb(the default: converted to sRGB) or:keep(the photograph's colour profile kept, for wide-gamut screens).:metadata-:none(the default: none of the original's EXIF, where it was taken least of all) or:all.:background-"#rrggbb", under transparency a format without it cannot keep. The default is white.:options- a renderer's own options, by renderer:options: [imgproxy: "sh:0.3"].
A preset changed gives its variants new URLs and keys: a CDN's copy, kept for a year, is never of an older preset. The variants of the old one stay until their original is deleted, or the prefix is.
Summary
Functions
file's variant of preset, found or made: {:ok, %Storehouse.File{}}.
The format of a preset's formats a browser is given, by its Accept
header: the first it takes, else the last.
The prefix of the variants of the file with key, or of the files under
a prefix: deleting it deletes them.
The URL of file's variant of preset.
Functions
@spec get(Storehouse.File.t() | String.t(), atom(), keyword()) :: {:ok, Storehouse.File.t()} | {:error, term()}
file's variant of preset, found or made: {:ok, %Storehouse.File{}}.
Made once, however many ask for it at the same time, across a cluster's
nodes: the first renders it, the others wait for it
({:error, :timeout} past :timeout). Made with a renderer that renders
(Storehouse.Renderer.render/4); one of URLs only gives
{:error, :not_supported}. A missing original gives {:error, :enoent}.
Its :width and :height are known when it was made by this call.
Options
:width- as forurl/3.:accept- the browser'sAcceptheader, for a preset of several formats; or:format, one of them. The default is the last.:adapter- the store, of the original and of its variants.- The configuration's, for this call.
The format of a preset's formats a browser is given, by its Accept
header: the first it takes, else the last.
iex> Storehouse.Variants.negotiate([:avif, :webp, :original], "image/webp,*/*")
:webp
iex> Storehouse.Variants.negotiate([:webp, :original], nil)
:original
The prefix of the variants of the file with key, or of the files under
a prefix: deleting it deletes them.
@spec url(Storehouse.File.t() | String.t(), atom(), keyword()) :: String.t()
The URL of file's variant of preset.
Options
:width- the width wanted: the preset's that is as wide, or the next one up, or its widest. The default is its widest.:focus- what a crop keeps, for this file (a photograph's own focus point): only with a renderer of URLs, which signs it.- The configuration's (
:presets,:renderer,:url), for this call.