Skip to content

Releases: Shopify/hydrogen

skeleton@2024.10.3

12 Dec 09:05
0bebf5d
Compare
Choose a tag to compare

Patch Changes

@shopify/create-hydrogen@5.0.13

12 Dec 09:05
0bebf5d
Compare
Choose a tag to compare

Patch Changes

skeleton@2024.10.2

11 Dec 17:13
c915b67
Compare
Choose a tag to compare

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

    1. 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;
    1. Update loadDeferredData function. We no longer need to load in all the variants. You can also remove VARIANTS_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,
    -  };
    }
    1. Remove the redirect logic in the loadCriticalData function and completely remove redirectToFirstVariant 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;
    -  }) {
    -    ...
    -  }
    1. 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,
    -  );
    1. 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);
    1. 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,
    +  });
    1. Remove the Await and Suspense from the ProductForm. 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>
    1. 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
                ...
Read more

@shopify/hydrogen@2024.10.1

11 Dec 17:13
c915b67
Compare
Choose a tag to compare

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 for getPaginationVariables() utility
    • When specified, pagination URL parameters are prefixed with the namespace (e.g., products_cursor instead of cursor)
    • Maintains backwards compatibility when no namespace is provided
  • Introduce getProductOptions, getAdjacentAndFirstAvailableVariants, useSelectedOptionInUrlParam, and mapSelectedProductOptionToObject 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 the VariantSelector 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

11 Dec 17:13
c915b67
Compare
Choose a tag to compare

Patch Changes

  • Introduce getProductOptions, getAdjacentAndFirstAvailableVariants, useSelectedOptionInUrlParam, and mapSelectedProductOptionToObject to support combined listing products and products with 2000 variants limit. (#2659) by @wizardlyhel

@shopify/create-hydrogen@5.0.12

11 Dec 17:13
c915b67
Compare
Choose a tag to compare

Patch Changes

@shopify/cli-hydrogen@9.0.3

11 Dec 17:13
c915b67
Compare
Choose a tag to compare

Patch Changes

skeleton@2024.10.1

30 Oct 21:00
c9a7e11
Compare
Choose a tag to compare

Patch Changes

@shopify/create-hydrogen@5.0.11

30 Oct 21:00
c9a7e11
Compare
Choose a tag to compare

Patch Changes

@shopify/cli-hydrogen@9.0.2

30 Oct 14:27
fcac4ab
Compare
Choose a tag to compare

Patch Changes