Simple React components for making use of images hosted on the Sanity.io CDN. I'm using Next.js and didn't really like the extra markup and apparent complexity of next/image so decided to try to create some simple components to provide some of that functionality at lower cognitive cost, and without being Next-specific. I'm not sure if this was a good decision. This package provides:
- An
Img
component that generatessrcset
values for a specified range of image widths (or a sensible defaults inDEFAULT_SOURCE_WIDTHS
). - Also accepts an optional
aspectRatio
prop to ensure generated images are cropped to a specific ratio. - A
Picture
component that wraps the above, providing amedia
prop allowing different aspect ratios to be specified for different media conditions, to satisfy the art-direction use-case. - Images support optional
lqip
prop which displays the image's Low Quality Image Placeholder as abackground-image
. - Images support WebP format using Sanity's automatic content negotiation.
- Images have
width
andheight
attributes set automatically based on the suppliedaspectRatio
or intrinsic size of the image, to prevent layout shifts.
This package is currently distributed in ES Module and CommonJS formats. This is my first npm package and I don't really know what I'm doing but those two options seemed popular. Structure of this package and README is inspired by next-sanity-image
, with thanks.
npm install --save @biggleszx/react-sanity-image
The components require you to pass in a SanityClient instance, so you'll also need to install that if you haven't already:
npm install --save @sanity/client
Import the component(s) you want to use:
const { Img, Picture } = require('@biggleszx/react-sanity-image');
// or
import { Img, Picture } from '@biggleszx/react-sanity-image';
Instantiate the Sanity client (see the SanityClient docs for more information about the properties used):
const client = sanityClient({
projectId: 'xxxxxxxx',
dataset: 'production',
apiVersion: '2022-03-14',
useCdn: true,
});
Assuming you've already queried a document from Sanity that includes an image field (there's an example here; let's say it's person
and the field is person.image
), render the Img
component like this:
// Basic usage at intrinsic aspect ratio
<Img
client={client}
image={person.image}
/>
// Passing options to the URL builder, including `sourceWidths` to override `DEFAULT_SOURCE_WIDTHS`
<Img
client={client}
image={person.image}
builderOptions={{
blur: 50,
sourceWidths: [100, 200, 300],
}}
/>
// Specifying aspect ratio and `sizes` attribute, and enabling `lqip`
<Img
client={client}
image={person.image}
aspectRatio={9/16}
sizes="50vw"
lqip
/>
// Any other props will be passed to the rendered `<img>` element
<Img
client={client}
image={person.image}
alt={person.name}
loading="lazy"
/>
The Picture
component is similar but supports an additional media
prop for adding extra sources:
<Picture
client={client}
image={person.image}
aspectRatio={1/1}
media={[{
media: '(min-width: 1024px)',
aspectRatio: 9/16,
}]}
sizes="(min-width: 1024px) 50vw, 100vw"
/>
It also supports builderOptions
like Img
does, which will be applied to all URLs that are created:
<Picture
client={client}
image={person.image}
builderOptions={{
blur: 50
}}
/>
Order of media
items matters the same way ordering source
elements inside <picture>
matters (i.e. the browser will use the first match that it encounters).
Sanity's CDN will automatically serve WebP format images to browsers that support them, so there's no need to include any extra sources for these (you can't specify the type
attribute at the moment anyway).
Finally, any extra props passed to Picture
will be set on the rendered <picture>
element. If you want to specify extra props for the <img>
element inside it, use imgProps
:
<Picture
client={client}
image={person.image}
className="my-picture-class"
imgProps={{
alt: person.name,
loading: 'lazy',
}}
/>
Clone the project, activate nvm
, if that's your thing, and install:
$ git clone [email protected]:BigglesZX/react-sanity-image.git
$ cd react-sanity-image
$ nvm use # `nvm install` if necessary
$ npm install
Storybook is included to facilitate local development and testing. You'll need to configure a connection to a Sanity project from which an image asset can be retrieved for use in stories.
Copy the included .env.example
file to .env
and edit it to add your project details and the ID of an asset to fetch. You can find one by querying imageAsset
documents within your project (*[_type == "sanity.imageAsset"]
).
SANITY_PROJECT_ID="xxxxxxxx"
SANITY_DATASET="production"
SANITY_ASSET_ID="image-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-1024x1024-jpg"
Add the contents of the .env
file to your environment using whatever means you like (dotenv, etc) – the simplest way is to source
the file in Bash-like shells:
$ source .env
Lastly you'll need to allow CORS access to Sanity from the Storybook instance. Head to the Sanity management dashboard, click on your project, then choose the API tab and click CORS origins. Click Add CORS origin and enter http://localhost:6006
(or other Storybook origin). You don't need to allow credentials.
Start Storybook:
$ npm run storybook
property | type | description |
---|---|---|
client |
SanityClient |
Client instance to use when building image URLs |
image |
SanityImageSource |
A reference to a Sanity image asset. You can pass in any asset that is also supported by the image() method of @sanity/image-url. |
aspectRatio |
number | null |
Aspect ratio (height ÷ width ) to which the source image should be cropped, e.g. 9/16 or 0.5625 for a 16:9 image. If omitted or set to null , the intrinsic aspect ratio of the source will be used. |
lqip |
boolean |
Set to true to use the image's Low Quality Image Placeholder as a placeholder (via CSS background-image ). Requires that lqip be enabled in the image field's metadata setting – I think this needs to be present at time of of upload, but maybe not in recent versions. |
sizes |
string |
String to use for the rendered <img> element's sizes attribute. See example in Usage above. |
builderOptions |
ImageUrlBuilderOptionsWithAliases |
The options that will be passed to the URL builder |
property | type | description |
---|---|---|
client |
SanityClient |
Client instance to use when building image URLs |
image |
SanityImageSource |
A reference to a Sanity image asset. You can pass in any asset that is also supported by the image() method of @sanity/image-url. |
aspectRatio |
number | null |
Aspect ratio (height ÷ width ) to which the source image should be cropped for the default source, i.e. if no media conditions match. If omitted or set to null , the intrinsic aspect ratio of the source will be used. |
lqip |
boolean |
Set to true to use the image's Low Quality Image Placeholder as a placeholder |
media |
[{ media: string, aspectRatio: number, sizes: string }] |
Specify an array of media conditions and aspect ratios which will be used to render <source> elements in the resulting <picture> . Order of items matters (the browser will use the first match it encounters). See example in Usage above. |
sizes |
string |
String to use for the rendered <img> element's sizes attribute. See example in Usage above. |
imgProps |
object |
Any extra props to pass through to the rendered <img> element |
builderOptions |
ImageUrlBuilderOptionsWithAliases |
The options that will be passed to the URL builder |
This is mostly for my benefit.
- Bump version number in
package.json
- Update
CHANGELOG
$ git commit ...
$ git tag -a x.x.x
(checkgit tag
for current)$ git push origin main
$ git push --tags
$ npm publish --access public
- Correct various as-yet unrealised bad decisions.