-
Add support for multiple schemas in GraphiQL. Fix links in Subrequest Profiler. (#1693) by @frandiox
-
♻️
CustomerClient
type is deprecated and replaced byCustomerAccount
(#1692) by @michenly -
Log GraphQL errors automatically in Storefront client, with a new
logErrors: boolean
option to disable it. Add back a link to GraphiQL in the error message. (#1690) by @frandiox
-
Better Hydrogen error handling (#1645) by @wizardlyhel
- Fix storefront client throwing on partial successful errors
- Fix subrequest profiler to better display network errors with URL information for Storefront API requests
This update changes the shape of the error objects returned by the
createCartHandler
method.Previously, mutations could return an
errors
array that contained auserErrors
array.With this change, these arrays are no longer nested. The response can contain both an
errors
array and auserErrors
array.errors
contains GraphQL execution errors.userErrors
contains errors caused by the cart mutation itself (such as adding a product that has zero inventory).storefront.isApiError
is deprecated.cart.get()
used to return aCart
type. Now it returnsCartReturn
type to accommodate theerrors
object.- All other
cart
methods (ie.cart.addLines
) used to return aCartQueryData
type. Now it returnsCartQueryDataReturn
type to accommodate theerrors
object.
-
Custom rules passed to
createContentSecurityPolicy
now extend the default Shopify and development domains, instead of overriding them (#1593) by @michenly -
Upgrade to Storefront API v2024-01 (#1642) by @wizardlyhel
-
Add Subrequest Profiler developer tool to enable better observability of server-side network requests and caching behaviors (#1511) by @wizardlyhel
-
Introduce the new
createCustomerAccountClient
for interacting with the Customer Account API (#1606) by @michenly
-
Fix a bug that allowed undesired redirect to external domains (#1629) by @wizardlyhel
-
Fix content security policy to recognize
localhost
asset server as a valid source when running thedev
command (#1591) by @michenly -
Fix the
<Seo />
component to render canonical URLs without trailing slashes. Thanks to @joshuafredrickson for reporting (#1622) by @blittle -
Make default
HydrogenSession
type extensible. (#1590) by @michenlyUpdate implementation of HydrogenSession using type:
import { + type HydrogenSession, } from '@shopify/hydrogen'; - class HydrogenSession { + class AppSession implements HydrogenSession { ... }
-
Fix error stack traces thrown by API clients if promises are not awaited (#1656) by @frandiox
-
Updated dependencies [
0629bc77
,dc8f90de
,ca1161b2
]:- @shopify/[email protected]
-
Fix the Pagination component to always restore scroll correctly on back/forth navigation. (#1508) by @blittle
-
Serve assets from a separate domain when running the dev server, to better simulate cross-domain behaviors. This makes it more realistic to work with CORS requests, content security policies, and CDN paths in development. (#1503) by @frandiox
-
Export caching types to make creating custom clients easier in TypeScript. (#1507) by @juanpprieto
-
Update the return types of the Customer Account API query and mutation methods. Also update Customer Account API default version to 2024-01. (#1537) by @blittle
-
Fix how peer dependencies are resolved. (#1489) by @frandiox
-
Add default
channel
value ofhydrogen
to Hydrogen’sShopPayButton
component. (#1447) by @QuintonC -
Updated dependencies [
848c6260
,62f67873
,e8cc49fe
]:- @shopify/[email protected]
- Change @remix-run/server-runtime to properly be a peer dependency by @blittle
- SEO component: remove URL params from canonical tags (#1478) by @scottdixon
Hydrogen 2023-10 has upgraded to Remix v2 and is now a peer dependency.
-
Please check the Remix v2 release notes to see what needs to be changed in your app code. Common changes include:
- Renaming types prefixed with
V2_
. For example,V2_MetaFunction
is nowMetaFunction
. - Renaming other types like
LoaderArgs
andActionArgs
, which are nowLoaderFunctionArgs
andActionFunctionArgs
respectively.
If you were not already using v2 flags, follow the official Remix migration guide before upgrading to v2.
- Renaming types prefixed with
-
Update to Remix v2. Remix is now a peer dependency and its version is no longer pinned. This means that you can upgrade to newer Remix 2.x versions without upgrading Hydrogen. (#1289) by @frandiox
-
The default caching strategy has been updated. The new default caching strategy provides a
max-age
value of 1 second, and astale-while-revalidate
value of 1 day. If you would keep the old caching values, update your queries to useCacheShort
: (#1336) by @benjaminsehlconst {product} = await storefront.query( `#graphql query Product($handle: String!) { product(handle: $handle) { id title } } `, { variables: {handle: params.productHandle}, + /** + * Override the default caching strategy with the old caching values + */ + cache: storefront.CacheShort(), }, );
-
The Storefront API types included are now generated using
@graphql-codegen/typescript@4
(changelog). This results in a breaking change if you were importingScalars
directly from@shopify/hydrogen-react
or@shopify/hydrogen
: (#1108) by @frandioximport type {Scalars} from '@shopify/hydrogen/storefront-api-types'; type Props = { - id: Scalars['ID']; // This was a string + id: Scalars['ID']['input']; // Need to access 'input' or 'output' to get the string };
-
Add a client to query the Customer Account API (#1430) by @blittle
-
Update Storefront API version to 2023-10 (#1431) by @wizardlyhel
-
Custom cart methods are now stable: (#1440) by @wizardlyhel
const cart = createCartHandler({ storefront, getCartId, setCartId: cartSetIdDefault(), - customMethods__unstable: { + customMethods: { addLines: async (lines, optionalParams) => { // ... }, }, });
-
Remove deprecated parameters and props (#1455 and #1435): (#1435) by @wizardlyhel
createStorefrontClient
parametersbuyerIp
andrequestGroupId
<Image>
propsloaderOptions
andwidths
-
Add query explorer plugin to GraphiQL. Start your dev server and load
http://localhost:3000/graphiql
to use GraphiQL. (#1470) by @frandiox -
Updated dependencies [
0ae7cbe2
,ad45656c
]:- @shopify/[email protected]
-
Fix template dist package due to CI error (#1451) by @wizardlyhel
-
Updated dependencies [
3eb376fe
]:- @shopify/[email protected]
-
Move
react
to peer dependencies. It had been added as a direct dependency by mistake in a previous version. (#1439) by @frandiox -
Integrate the debug-network tooling with the new
--worker-unstable
runtime CLI flag. (#1387) by @frandiox -
Calls to
withCache
can now be shown in the/debug-network
tool when using the Worker runtime. For this to work, use the newrequest
parameter increateWithCache
: (#1438) by @frandioxexport default { fetch(request, env, executionContext) { // ... const withCache = createWithCache({ cache, waitUntil, + request, }); // ... }, }
-
Updated dependencies [
d30e2651
,1b45311d
,2627faa7
]:- @shopify/[email protected]
-
Allow generic inference in standalone usage of WithCache type - Contributed by @chinanderm (#1363) by @wizardlyhel
-
Cart Optimistic UI helpers (#1366) by @wizardlyhel
-
Fix storefront sub request cache key (#1375) by @wizardlyhel
-
Fix the Pagination component to use forwardRefs for the NextLink and PreviousLink render props (#1362) by @blittle
-
The
error.cause
property throw from the Storefront client is now stringified. (#1184) by @frandiox -
Fix Hydrogen's Storefront API client to not throw unhandled promise exceptions. This is because Remix is guaranteed to handle exceptions from the loader and fixing it prevents Hydrogen from crashing when deployed to some runtimes on unhandled promise exceptions. (#1318) by @blittle
-
Relax prop validation on the
getSelectedProductOptions
andgetSelectedProductOptions
utilities to look for member props instead of checking withinstanceof
. (#1327) by @blittle
-
Supress the hydration warning in the new
<Script>
component whennonce
values differ between the server and client, which is expected. (#1312) by @frandiox -
(Unstable) server-side network request debug virtual route (#1284) by @wizardlyhel
-
Update your
server.ts
so that it also passes in thewaitUntil
andenv
.const handleRequest = createRequestHandler({ build: remixBuild, mode: process.env.NODE_ENV, + getLoadContext: () => ({session, storefront, env, waitUntil}), });
If you are using typescript, make sure to update
remix.env.d.ts
declare module '@shopify/remix-oxygen' { export interface AppLoadContext { + env: Env; cart: HydrogenCart; storefront: Storefront; session: HydrogenSession; + waitUntil: ExecutionContext['waitUntil']; } }
-
Run
npm run dev
and you should see terminal log information about a new virtual route that you can view server-side network requests at http://localhost:3000/debug-network -
Open http://localhost:3000/debug-network in a tab and your app another tab. When you navigate around your app, you should see server network requests being logged in the debug-network tab
-
- Updated dependencies [
345f06a2
]:- @shopify/[email protected]
-
Fix the Pagination component to reset internal state when the URL changes (not including Pagination params). (#1291) by @blittle
We also now validate the connection prop to include a
pageInfo
object with the following properties:hasNextPage
hasPreviousPage
endCursor
startCursor
Previously our templates had a bug where
startCursor
was not included. Upgrading means the app will error until you update your query to include it:query CollectionDetails { collection(handle: $handle) { ... pageInfo { hasPreviousPage hasNextPage hasNextPage endCursor + startCursor } } }
-
Fix hydration errors and stale data within the Pagination component (#1283) by @blittle
-
Add custom product paths to the
VariantSelector
component: (#1271) by @blittle<VariantSelector handle="snowboard" productPath="shop" options={options}> {/* ... */} </VariantSelector>
-
Add functionality for creating a Content Security Policy. See the guide on Content Security Policies for more details. (#1235) by @blittle
-
Updated dependencies [
06516ee9
,423acee2
]:- @shopify/[email protected]
-
Exported the type
CookieOptions
fromcartSetIdDefault
(#1153) by @remcolakens -
Updated dependencies [
e9e1736a
,1a0e858d
]:- @shopify/[email protected]
-
Surface storefront api response errors (#1205) by @wizardlyhel
-
Updated dependencies [
d80c4ada
]:- @shopify/[email protected]
⭐️ Check out our blog post with all the latest updates on Hydrogen, and what’s coming on the roadmap.
The latest version of Hydrogen comes with new and updated components and utilities that can help you speed up your build:
- An updated server-side Cart component with built-in abstractions to handle most common cart operations, including adding, updating, or deleting line items, applying discounts, and more.
- A drop-in
<Pagination/>
component to make it easier to handle large product collections. - A new
<VariantSelector/>
component that makes it faster to build progressively enhanced product forms. - Improved support for predictive search and local pickup options through Storefront API version 2023-07.
-
createWithCache
is now stable. All imports need to be updated: (#1151) by @blittle- import {createWithCache_unstable} from '@shopify/hydrogen'; + import {createWithCache} from '@shopify/hydrogen';
-
Pagination
andgetPaginationVariables
are now stable. (#1129) by @blittleAll imports to each should be updated:
- import {Pagiatinon__unstable, getPaginationVariables__unstable} from '@shopify/hydrogen'; + import {Pagiatinon, getPaginationVariables} from '@shopify/hydrogen';
-
Function and component for cart management: (#786) by @wizardlyhel
createCartHandler
- Creates an object instance that simplifies cart operations such as add/update/remove from cart.CartForm
- A form component that helps you sets up form inputs for cart handler.
Documentation:
- Updated how-to guides
createCartHandler
CartForm
-
Export useLoadScript (#1080) by @wizardlyhel
-
Throw error when
storeDomain
is not passed tocreateStorefrontClient
. (#1128) by @frandiox -
Improve warning and error format for known Hydrogen messages in development. (#1093) by @frandiox
-
Add an example using the new Customer Account API (#1126) by @blittle
-
Corrected the
$attributes
type inCART_ATTRIBUTES_UPDATE_MUTATION
to match the expected one (#1117) by @remcolakens -
Fix cache key by url encode the sub request keys (#1105) by @wizardlyhel
Add a <VariantSelector>
component to make building product forms easier. Also added the getSelectedProductOptions
helper function. See the guide on using the VariantSelector. (#1027) by @blittle
- Updated dependencies [
b8f41ad7
]:- @shopify/[email protected]
-
Update Remix to the latest version (
1.17.1
). (#852) by @frandioxWhen updating your app, remember to also update your Remix dependencies to
1.17.1
in yourpackage.json
file:-"@remix-run/react": "1.15.0", +"@remix-run/react": "1.17.1", -"@remix-run/dev": "1.15.0", -"@remix-run/eslint-config": "1.15.0", +"@remix-run/dev": "1.17.1", +"@remix-run/eslint-config": "1.17.1",
-
A default
https://
protocol is now added automatically tostoreDomain
if missing. (#985) by @frandiox -
Fix
flattenConnection()
's TypeScript types when working withedges.node
(#945) by @frehner -
Make
storefrontApiVersion
parameter optional. By default, it will use the current version of Hydrogen as the Storefront API version. (#984) by @frandiox -
Skip reading and writing cache in sub-requests when the strategy is CacheNone. (#964) by @frandiox
-
Fix
<ModelViewer>
to properly set className (#966) by @blittle -
Add a
/admin
route that redirects to the Shopify admin. This redirect can be disabled by passingnoAdminRedirect: true
tostorefrontRedirect
: (#989) by @blittlestorefrontRedirect({ redirect, response, storefront, noAdminRedirect: true, });
-
Updated dependencies [
7b4afea2
,32515232
,7d6a1a7c
,442f602a
,b9ab8eb7
,93a7c3c6
]:- @shopify/[email protected]
-
Updated dependencies [
7aaa4e86
]:- @shopify/[email protected]
-
Add support for generated types from the new unstable codegen feature in the CLI. (#707) by @frandiox
-
Add a
<Pagination__unstable>
component andgetPaginationVariables__unstable
helper to make rendering large lists from the Storefront API easy. This is an initial unstable release and we expect to finalize the API by the 2023-07 release. See the<Pagination>
component documentation. (#755) by @cartogram -
Updated dependencies [
2e1e4590
]:- @shopify/[email protected]
-
Adds
parseGid()
which is a helper function that takes in a Shopify GID and returns theresource
andid
from it. For example: (#845) by @frehnerimport {parseGid} from '@shopify/hydrogen-react'; const {id, resource} = parseGid('gid://shopify/Order/123'); console.log(id); // 123 console.log(resource); // Order
-
Avoid warning about missing
buyerIp
when using private access tokens in development. (#836) by @frandiox -
Updated dependencies [
0a009a3b
]:- @shopify/[email protected]
-
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