Releases: Shopify/hydrogen
skeleton@2024.10.3
Patch Changes
- Prevent scroll reset on variant change (#2672) by @scottdixon
@shopify/create-hydrogen@5.0.13
Patch Changes
- Prevent scroll reset on variant change (#2672) by @scottdixon
skeleton@2024.10.2
Patch Changes
-
Remove initial redirect from product display page (#2643) by @scottdixon
-
Optional updates for the product route and product form to handle combined listing and 2000 variant limit. (#2659) by @wizardlyhel
- Update your SFAPI product query to bring in the new query fields:
const PRODUCT_FRAGMENT = `#graphql fragment Product on Product { id title vendor handle descriptionHtml description + encodedVariantExistence + encodedVariantAvailability options { name optionValues { name + firstSelectableVariant { + ...ProductVariant + } + swatch { + color + image { + previewImage { + url + } + } + } } } - selectedVariant: selectedOrFirstAvailableVariant(selectedOptions: $selectedOptions, ignoreUnknownOptions: true, caseInsensitiveMatch: true) { + selectedOrFirstAvailableVariant(selectedOptions: $selectedOptions, ignoreUnknownOptions: true, caseInsensitiveMatch: true) { + ...ProductVariant + } + adjacentVariants (selectedOptions: $selectedOptions) { + ...ProductVariant + } - variants(first: 1) { - nodes { - ...ProductVariant - } - } seo { description title } } ${PRODUCT_VARIANT_FRAGMENT} ` as const;
- Update
loadDeferredData
function. We no longer need to load in all the variants. You can also removeVARIANTS_QUERY
variable.
function loadDeferredData({context, params}: LoaderFunctionArgs) { + // Put any API calls that is not critical to be available on first page render + // For example: product reviews, product recommendations, social feeds. - // In order to show which variants are available in the UI, we need to query - // all of them. But there might be a *lot*, so instead separate the variants - // into it's own separate query that is deferred. So there's a brief moment - // where variant options might show as available when they're not, but after - // this deferred query resolves, the UI will update. - const variants = context.storefront - .query(VARIANTS_QUERY, { - variables: {handle: params.handle!}, - }) - .catch((error) => { - // Log query errors, but don't throw them so the page can still render - console.error(error); - return null; - }); + return {} - return { - variants, - }; }
- Remove the redirect logic in the
loadCriticalData
function and completely removeredirectToFirstVariant
function
async function loadCriticalData({ context, params, request, }: LoaderFunctionArgs) { const {handle} = params; const {storefront} = context; if (!handle) { throw new Error('Expected product handle to be defined'); } const [{product}] = await Promise.all([ storefront.query(PRODUCT_QUERY, { variables: {handle, selectedOptions: getSelectedProductOptions(request)}, }), // Add other queries here, so that they are loaded in parallel ]); if (!product?.id) { throw new Response(null, {status: 404}); } - const firstVariant = product.variants.nodes[0]; - const firstVariantIsDefault = Boolean( - firstVariant.selectedOptions.find( - (option: SelectedOption) => - option.name === 'Title' && option.value === 'Default Title', - ), - ); - if (firstVariantIsDefault) { - product.selectedVariant = firstVariant; - } else { - // if no selected variant was returned from the selected options, - // we redirect to the first variant's url with it's selected options applied - if (!product.selectedVariant) { - throw redirectToFirstVariant({product, request}); - } - } return { product, }; } ... - function redirectToFirstVariant({ - product, - request, - }: { - product: ProductFragment; - request: Request; - }) { - ... - }
- Update the
Product
component to use the new data fields.
import { getSelectedProductOptions, Analytics, useOptimisticVariant, + getAdjacentAndFirstAvailableVariants, } from '@shopify/hydrogen'; export default function Product() { + const {product} = useLoaderData<typeof loader>(); - const {product, variants} = useLoaderData<typeof loader>(); + // Optimistically selects a variant with given available variant information + const selectedVariant = useOptimisticVariant( + product.selectedOrFirstAvailableVariant, + getAdjacentAndFirstAvailableVariants(product), + ); - const selectedVariant = useOptimisticVariant( - product.selectedVariant, - variants, - );
- Handle missing search query param in url from selecting a first variant
import { getSelectedProductOptions, Analytics, useOptimisticVariant, getAdjacentAndFirstAvailableVariants, + useSelectedOptionInUrlParam, } from '@shopify/hydrogen'; export default function Product() { const {product} = useLoaderData<typeof loader>(); // Optimistically selects a variant with given available variant information const selectedVariant = useOptimisticVariant( product.selectedOrFirstAvailableVariant, getAdjacentAndFirstAvailableVariants(product), ); + // Sets the search param to the selected variant without navigation + // only when no search params are set in the url + useSelectedOptionInUrlParam(selectedVariant.selectedOptions);
- Get the product options array using
getProductOptions
import { getSelectedProductOptions, Analytics, useOptimisticVariant, + getProductOptions, getAdjacentAndFirstAvailableVariants, useSelectedOptionInUrlParam, } from '@shopify/hydrogen'; export default function Product() { const {product} = useLoaderData<typeof loader>(); // Optimistically selects a variant with given available variant information const selectedVariant = useOptimisticVariant( product.selectedOrFirstAvailableVariant, getAdjacentAndFirstAvailableVariants(product), ); // Sets the search param to the selected variant without navigation // only when no search params are set in the url useSelectedOptionInUrlParam(selectedVariant.selectedOptions); + // Get the product options array + const productOptions = getProductOptions({ + ...product, + selectedOrFirstAvailableVariant: selectedVariant, + });
- Remove the
Await
andSuspense
from theProductForm
. We no longer have any queries that we need to wait for.
export default function Product() { ... return ( ... + <ProductForm + productOptions={productOptions} + selectedVariant={selectedVariant} + /> - <Suspense - fallback={ - <ProductForm - product={product} - selectedVariant={selectedVariant} - variants={[]} - /> - } - > - <Await - errorElement="There was a problem loading product variants" - resolve={variants} - > - {(data) => ( - <ProductForm - product={product} - selectedVariant={selectedVariant} - variants={data?.product?.variants.nodes || []} - /> - )} - </Await> - </Suspense>
- Update the
ProductForm
component.
import {Link, useNavigate} from '@remix-run/react'; import {type MappedProductOptions} from '@shopify/hydrogen'; import type { Maybe, ProductOptionValueSwatch, } from '@shopify/hydrogen/storefront-api-types'; import {AddToCartButton} from './AddToCartButton'; import {useAside} from './Aside'; import type {ProductFragment} from 'storefrontapi.generated'; export function ProductForm({ productOptions, selectedVariant, }: { productOptions: MappedProductOptions[]; selectedVariant: ProductFragment['selectedOrFirstAvailableVariant']; }) { const navigate = useNavigate(); const {open} = useAside(); return ( <div className="product-form"> {productOptions.map((option) => ( <div className="product-options" key={option.name}> <h5>{option.name}</h5> <div className="product-options-grid"> {option.optionValues.map((value) => { const { name, handle, variantUriQuery, selected, available, exists, isDifferentProduct, swatch, } = value; if (isDifferentProduct) { // SEO // When the variant is a combined listing child product // that leads to a different url, we need to render it // as an anchor tag ...
@shopify/hydrogen@2024.10.1
Patch Changes
-
Added namespace support to prevent conflicts when using multiple Pagination components: (#2649) by @scottdixon
- New optional
namespace
prop for the<Pagination/>
component - New optional
namespace
option forgetPaginationVariables()
utility - When specified, pagination URL parameters are prefixed with the namespace (e.g.,
products_cursor
instead ofcursor
) - Maintains backwards compatibility when no namespace is provided
- New optional
-
Introduce
getProductOptions
,getAdjacentAndFirstAvailableVariants
,useSelectedOptionInUrlParam
, andmapSelectedProductOptionToObject
to support combined listing products and products with 2000 variants limit. (#2659) by @wizardlyhel -
Add params to override the login and authorize paths: (#2648) by @blittle
const hydrogenContext = createHydrogenContext({ // ... customerAccount: { loginPath = '/account/login', authorizePath = '/account/authorize', defaultRedirectPath = '/account', }, });
-
Add
selectedVariant
prop to theVariantSelector
to use for the initial state if no URL parameters are set (#2643) by @scottdixon -
Updated dependencies [
a57d5267
]:- @shopify/hydrogen-react@2024.10.1
@shopify/hydrogen-react@2024.10.1
Patch Changes
- Introduce
getProductOptions
,getAdjacentAndFirstAvailableVariants
,useSelectedOptionInUrlParam
, andmapSelectedProductOptionToObject
to support combined listing products and products with 2000 variants limit. (#2659) by @wizardlyhel
@shopify/create-hydrogen@5.0.12
Patch Changes
- Update to skeleton template by @wizardlyhel
@shopify/cli-hydrogen@9.0.3
Patch Changes
- Attach Hydrogen version metadata to deployments (#2645) by @benwolfram
skeleton@2024.10.1
Patch Changes
- Bump to get new cli package version by @wizardlyhel
@shopify/create-hydrogen@5.0.11
Patch Changes
- Bump to get new cli package version by @wizardlyhel