Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Image component (2023-04) (#787)
* Creates new Image on top of 2023-04 Co-authored-by: Matt Seccafien <matt.seccafien@shopify.com> * Adds changeset * Fix tests * Builds docs * update changesets to major * Update .changeset/thirty-rice-sin.md Co-authored-by: Matt Seccafien <matt.seccafien@shopify.com> * fix docs * Apply suggestions from code review Co-authored-by: Matt Seccafien <matt.seccafien@shopify.com> * Add default width to and update docs * Updates Image component and reverts collection template * Remove todo from demo store * simplify collection card image implementation * Updates changeset --------- Co-authored-by: Matt Seccafien <matt.seccafien@shopify.com> Co-authored-by: Daniel Rios Pavia <daniel.riospavia@shopify.com>
- Loading branch information
1 parent
f991a42
commit 361879e
Showing
25 changed files
with
1,470 additions
and
876 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,4 @@ | |
'@shopify/hydrogen-react': major | ||
--- | ||
|
||
Release 2023-04 | ||
Releases 2023-04 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
--- | ||
'@shopify/hydrogen-react': major | ||
'@shopify/hydrogen': major | ||
--- | ||
|
||
Adds a new `Image` component, replacing the existing one. While your existing implementation won't break, props `widths` and `loaderOptions` are now deprecated disregarded, with a new `aspectRatio` prop added. | ||
|
||
### Migrating to the new `Image` | ||
|
||
The new `Image` component is responsive by default, and requires less configuration to ensure the right image size is being rendered on all screen sizes. | ||
|
||
**Before** | ||
|
||
```jsx | ||
<Image | ||
data={image} | ||
widths={[400, 800, 1200]} | ||
width="100px" | ||
sizes="90vw" | ||
loaderOptions={{ | ||
scale: 2, | ||
crop: 'left', | ||
}} | ||
/> | ||
``` | ||
|
||
**After** | ||
|
||
```jsx | ||
<Image data={image} sizes="90vw" crop="left" aspectRatio="3/2" /> | ||
``` | ||
|
||
Note that `widths` and `loaderOptions` have now been deprecated, declaring `width` is no longer necessary, and we’ve added an `aspectRatio` prop: | ||
|
||
- `widths` is now calculated automatically based on a new `srcSetOptions` prop (see below for details). | ||
- `loaderOptions` has been removed in favour of declaring `crop` and `src` as props. `width` and `height` should only be set as props if rendering a fixed image size, with `width` otherwise defaulting to `100%`, and the loader calculating each dynamically. | ||
- `aspectRatio` is calculated automatically using `data.width` and `data.height` (if available) — but if you want to present an image with an aspect ratio other than what was uploaded, you can set using the format `Int/Int` (e.g. `3/2`, [see MDN docs for more info](https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio), note that you must use the _fraction_ style of declaring aspect ratio, decimals are not supported); if you've set an `aspectRatio`, we will default the crop to be `crop: center` (in the example above we've specified this to use `left` instead). | ||
|
||
### Examples | ||
|
||
<!-- Simplest possible usage --> | ||
|
||
#### Basic Usage | ||
|
||
```jsx | ||
<Image data={data} /> | ||
``` | ||
|
||
This would use all default props, which if exhaustively declared would be the same as typing: | ||
|
||
```jsx | ||
<Image | ||
data={data} | ||
crop="center" | ||
decoding="async" | ||
loading="lazy" | ||
width="100%" | ||
sizes="100vw" | ||
srcSetOptions={{ | ||
interval: 15, | ||
startingWidth: 200, | ||
incrementSize: 200, | ||
placeholderWidth: 100, | ||
}} | ||
/> | ||
``` | ||
|
||
An alternative way to write this without using `data` would be to use the `src`, `alt`, and `aspectRatio` props. For example: | ||
|
||
```jsx | ||
<Image | ||
src={data.url} | ||
alt={data.altText} | ||
aspectRatio={`${data.width}/${data.height}`} | ||
/> | ||
``` | ||
|
||
Assuming `data` had the following shape: | ||
|
||
```json | ||
{ | ||
"url": "https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg", | ||
"altText": "alt text", | ||
"width": "4000", | ||
"height": "4000" | ||
} | ||
``` | ||
|
||
All three above examples would result in the following HTML: | ||
|
||
```html | ||
<img | ||
srcset="https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=300&height=300&crop=center 300w, … *13 additional sizes* … https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=3000&height=3000&crop=center 3000w" | ||
src="https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=100&height=100&crop=center" | ||
alt="alt text" | ||
sizes="100vw" | ||
loading="lazy" | ||
decoding="async" | ||
width="100px" | ||
height="100px" | ||
style="aspect-ratio: 4000 / 4000;" | ||
/> | ||
``` | ||
|
||
#### Fixed-size Images | ||
|
||
When using images that are meant to be a fixed size, like showing a preview image of a product in the cart, instead of using `aspectRatio`, you'll instead declare `width` and `height` manually with fixed values. For example: | ||
|
||
```jsx | ||
<Image data={data} width={80} height={80} /> | ||
``` | ||
|
||
Instead of generating 15 images for a broad range of screen sizes, `Image` will instead only generate 3, for various screen pixel densities (1x, 2x, and 3x). The above example would result in the following HTML: | ||
|
||
```html | ||
<img | ||
srcset=" | ||
https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=80&height=80&crop=center 1x, | ||
https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=160&height=160&crop=center 2x, | ||
https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=240&height=240&crop=center 3x | ||
" | ||
src="https://cdn.shopify.com/s/files/1/0551/4566/0472/products/Main.jpg?width=80&height=80" | ||
alt="alt text" | ||
loading="lazy" | ||
width="80px" | ||
height="80px" | ||
style="aspect-ratio: 80 / 80;" | ||
/> | ||
``` | ||
|
||
If you don't want to have a fixed aspect ratio, and instead respect whatever is returned from your query, the following syntax can also be used: | ||
|
||
```jsx | ||
<Image data={data} width="5rem" /> | ||
``` | ||
|
||
Which would result in the same HTML as above, however the generated URLs inside the `src` and `srcset` attributes would not have `height` or `crop` parameters appended to them, and the generated `aspect-ratio` in `style` would be `4000 / 4000` (if using the same `data` values as our original example). | ||
|
||
#### Custom Loaders | ||
|
||
If your image isn't coming from the Storefront API, but you still want to take advantage of the `Image` component, you can pass a custom `loader` prop, provided the CDN you're working with supports URL-based transformations. | ||
|
||
The `loader` is a function which expects a `params` argument of the following type: | ||
|
||
```ts | ||
type LoaderParams = { | ||
/** The base URL of the image */ | ||
src?: ImageType['url']; | ||
/** The URL param that controls width */ | ||
width?: number; | ||
/** The URL param that controls height */ | ||
height?: number; | ||
/** The URL param that controls the cropping region */ | ||
crop?: Crop; | ||
}; | ||
``` | ||
|
||
Here is an example of using `Image` with a custom loader function: | ||
|
||
```jsx | ||
const customLoader = ({src, width, height, crop}) => { | ||
return `${src}?w=${width}&h=${height}&gravity=${crop}`; | ||
}; | ||
|
||
export default function CustomImage(props) { | ||
<Image loader={customLoader} {...props} />; | ||
} | ||
|
||
// In Use: | ||
|
||
<CustomImage data={customCDNImageData} />; | ||
``` | ||
|
||
If your CDN happens to support the same semantics as Shopify (URL params of `width`, `height`, and `crop`) — the default loader will work a non-Shopify `src` attribute. | ||
|
||
An example output might look like: `https://mycdn.com/image.jpeg?width=100&height=100&crop=center` | ||
|
||
### Additional changes | ||
|
||
- Added the `srcSetOptions` prop used to create the image URLs used in `srcset`. It’s an object with the following keys and defaults: | ||
|
||
```js | ||
srcSetOptions = { | ||
intervals: 15, // The number of sizes to generate | ||
startingWidth: 200, // The smalles image size | ||
incrementSize: 200, // The increment by to increase for each size, in pixesl | ||
placeholderWidth: 100, // The size used for placeholder fallback images | ||
}; | ||
``` | ||
|
||
- Added an export for `IMAGE_FRAGMENT`, which can be imported from Hydrogen and used in any Storefront API query, which will fetch the required fields needed by the component. | ||
|
||
- Added an export for `shopifyLoader` for using Storefront API responses in conjunction with alternative frameworks that already have their own `Image` component, like Next.js |
Oops, something went wrong.