-
Releases
2023-04
(#754) by @lordofthecactus -
Updates Hydrogen to Storefront 2023-04 API release.
-
Updates types from
CartLineConnection
toBaseCartLineConnection
. -
Deprecates
CartLinePrice
from@shopify/hydrogen-react
useMoney
instead:- import {CartLinePrice} from '@shopify/hydrogen-react'; + import {Money} from '@shopify/hydrogen-react';
- <CartLinePrice line={line} /> + <Money data={line.priceV2} />
-
Adds a new
Image
component, replacing the existing one. While your existing implementation won't break, propswidths
andloaderOptions
are now deprecated disregarded, with a newaspectRatio
prop added. (#787) by @benjaminsehlThe 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
<Image data={image} widths={[400, 800, 1200]} width="100px" sizes="90vw" loaderOptions={{ scale: 2, crop: 'left', }} />
After
<Image data={image} sizes="90vw" crop="left" aspectRatio="3/2" />
Note that
widths
andloaderOptions
have now been deprecated, declaringwidth
is no longer necessary, and we’ve added anaspectRatio
prop:widths
is now calculated automatically based on a newsrcSetOptions
prop (see below for details).loaderOptions
has been removed in favour of declaringcrop
andsrc
as props.width
andheight
should only be set as props if rendering a fixed image size, withwidth
otherwise defaulting to100%
, and the loader calculating each dynamically.aspectRatio
is calculated automatically usingdata.width
anddata.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 formatInt/Int
(e.g.3/2
, see MDN docs for more info, note that you must use the fraction style of declaring aspect ratio, decimals are not supported); if you've set anaspectRatio
, we will default the crop to becrop: center
(in the example above we've specified this to useleft
instead).
<Image data={data} />
This would use all default props, which if exhaustively declared would be the same as typing:
<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 thesrc
,alt
, andaspectRatio
props. For example:<Image src={data.url} alt={data.altText} aspectRatio={`${data.width}/${data.height}`} />
Assuming
data
had the following shape:{ "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:
<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;" />
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 declarewidth
andheight
manually with fixed values. For example:<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:<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:
<Image data={data} width="5rem" />
Which would result in the same HTML as above, however the generated URLs inside the
src
andsrcset
attributes would not haveheight
orcrop
parameters appended to them, and the generatedaspect-ratio
instyle
would be4000 / 4000
(if using the samedata
values as our original example).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 customloader
prop, provided the CDN you're working with supports URL-based transformations.The
loader
is a function which expects aparams
argument of the following type: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: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
, andcrop
) — the default loader will work a non-Shopifysrc
attribute.An example output might look like:
https://mycdn.com/image.jpeg?width=100&height=100&crop=center
-
Added the
srcSetOptions
prop used to create the image URLs used insrcset
. It’s an object with the following keys and defaults: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 ownImage
component, like Next.js
- Updated dependencies [
82b6af7
,361879e
]:- @shopify/[email protected]
-
Bump internal Remix dependencies to 1.15.0. (#728) by @wizardlyhel
Recommendations to follow:
- Upgrade all the Remix packages in your app to 1.15.0.
- Enable Remix v2 future flags at your earliest convenience following the official guide.
-
Add an experimental
createWithCache_unstable
utility, which creates a function similar touseQuery
from Hydrogen v1. Use this utility to query third-party APIs and apply custom cache options. (#600) by @frandioxTo setup the utility, update your
server.ts
:import { createStorefrontClient, createWithCache_unstable, CacheLong, } from '@shopify/hydrogen'; // ... const cache = await caches.open('hydrogen'); const withCache = createWithCache_unstable({cache, waitUntil}); // Create custom utilities to query third-party APIs: const fetchMyCMS = (query) => { // Prefix the cache key and make it unique based on arguments. return withCache(['my-cms', query], CacheLong(), () => { const cmsData = await (await fetch('my-cms.com/api', { method: 'POST', body: query })).json(); const nextPage = (await fetch('my-cms.com/api', { method: 'POST', body: cmsData1.nextPageQuery, })).json(); return {...cmsData, nextPage} }); }; const handleRequest = createRequestHandler({ build: remixBuild, mode: process.env.NODE_ENV, getLoadContext: () => ({ session, waitUntil, storefront, env, fetchMyCMS, }), });
Note: The utility is unstable and subject to change before stabalizing in the 2023.04 release.
-
Updated dependencies [
85ae63a
,5e26503
]:- @shopify/[email protected]
-
Add new
loader
API for setting seo tags within route module (#591) by @cartogram -
ShopPayButton
component now can receive astoreDomain
. The component now does not requireShopifyProvider
. (#645) by @lordofthecactus -
Added
robots
option to SEO config that allows users granular control over the robots meta tag. This can be set on both a global and per-page basis using the handle.seo property. (#572) by @cartogramExample:
export handle = { seo: { robots: { noIndex: false, noFollow: false, } } }
-
Fix active cart session event in Live View (#614) by @wizardlyhel
Introducing
getStorefrontHeaders
that collects the required Shopify headers for making a Storefront API call.- Make cart constants available as exports from
@shopify/hydrogen-react
- Deprecating
buyerIp
andrequestGroupId
props fromcreateStorefrontClient
from@shopify/hydrogen
- Deprecating
getBuyerIp
function from@shopify/remix-oxygen
+ import {getStorefrontHeaders} from '@shopify/remix-oxygen'; import {createStorefrontClient, storefrontRedirect} from '@shopify/hydrogen'; export default { async fetch( request: Request, env: Env, executionContext: ExecutionContext, ): Promise<Response> { const {storefront} = createStorefrontClient({ cache, waitUntil, - buyerIp: getBuyerIp(request), i18n: {language: 'EN', country: 'US'}, publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN, privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN, storeDomain: `https://${env.PUBLIC_STORE_DOMAIN}`, storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION || '2023-01', storefrontId: env.PUBLIC_STOREFRONT_ID, - requestGroupId: request.headers.get('request-id'), + storefrontHeaders: getStorefrontHeaders(request), });
- Make cart constants available as exports from
-
Updated dependencies [
c78f441
,7fca5d5
]:- @shopify/[email protected]
- Fix template imports to only reference
@shopify/hydrogen
, not@shopify/hydrogen-react
(#523) by @blittle
-
Send Hydrogen version in Storefront API requests. (#471) by @frandiox
-
Fix default Storefront type in LoaderArgs. (#496) by @frandiox
- Initial release