Skip to content

Latest commit

 

History

History
2830 lines (1940 loc) · 154 KB

File metadata and controls

2830 lines (1940 loc) · 154 KB

Changelog

1.6.6

Patch Changes

  • Fix race condition and memory leak within Hydrogen Analytics (#2443) by @blittle

1.6.5

Patch Changes

1.6.4

Patch Changes

  • Improve form props type for render function (#2352) by @ya-s-u
  • Fix streaming SSR with multibyte characters (#2351) by @ya-s-u
  • Use correct defaultCartFragment in CartProvider.client.tsx This resolves an error sending Add To Cart events to Shopify Analytics (#2332) by @mperreux

1.6.3

Patch Changes

1.6.2

Patch Changes

  • Add a helper method to get headers to proxy the online store. These headers are necessary to prevent the online store from throttling proxied requests: (#2300) by @blittle

    import {getOnlineStorefrontHeaders} from '@shopify/hydrogen';
    
    async function handleEvent(event) {
      const response = fetch(`https://hydrogen.shop/products/hydrogen`, {
        headers: getOnlineStorefrontHeaders(event.request),
      });
    
      return response;
    }
  • Remove automatic origin support from fetchSync on the server. (#2276) by @jplhomer

    Important: Please test that your Hydrogen app is using fetchSync correctly before shipping this version of Hydrogen to production.

    Developers should never be making fetch requests on the server against their own Hydrogen app. This is because some production runtimes prohibit invoking fetch requests to servers in the same region. Other runtimes will fail to resolve DNS when invoked from within the same process.

    This change makes it required to pass a fully-qualified URL (including origin) to fetchSync when it's being used on the server:

    // MyComponent.server.jsx
    
    // ❌ You should not use this pattern, and it will now fail:
    fetchSync('/api/path').json();

    Instead, you should query the data directly, or extract the data query to a function and call it inside your server component:

    // MyComponent.server.jsx
    import {sharedQuery} from './shared-location.server';
    
    // ✅ Do this instead:
    useQuery('shared-query', sharedQuery);

    This is not considered a breaking change because the intention of the server-side fetchSync API was never to enable calling a Hydrogen app from itself, but rather to call third-party APIs from the server.

  • Fix issue with preload cache which would cause problems when scaled to large amounts of traffic in production. (#2316) by @jplhomer
  • Add more UAs to the bot detection list to improve parity with SEO tools (#2297) by @davecyen
  • Fix RSC responses in some situations where the pathname contains double quotes. (#2320) by @frandiox
  • Fix unhandled errors when parsing invalid RSC states from URL. (#2315) by @frandiox

1.6.1

Hydrogen UI React

We’re excited to announce an experimental version of Hydrogen UI React is now available! We’ve decoupled key components, hooks, and utilities from the Hydrogen framework and moved them into a new package called @shopify/hydrogen-react.

You can now bring Hydrogen functionality to your React framework of choice!

The components, hooks, and utilities will continue to be supported in @shopify/hydrogen until Hydrogen UI React is made generally available, but all new features will only go into @shopify/hydrogen-react going forward.

Check out the documentation and the repository to learn more!

Patch Changes

  • Allow basePath to be passed to Route to support path-based internationalization inside custom route (#2252) by @jplhomer

1.6.0

Minor Changes

  • Updates CartProvider to use the new CartProviderV2. No code changes are necessary. Docs for CartProvider can be found here. (#2182) by @lordofthecactus

    Also adds the following onComplete props callbacks:

    Name Type Description
    onCreateComplete? () => void Invoked when the process to create a cart completes successfully
    onLineAddComplete? () => void Invoked when the process to add a line item to the cart completes successfully
    onLineRemoveComplete? () => void Invoked when the process to remove a line item to the cart completes successfully
    onLineUpdateComplete? () => void Invoked when the process to update a line item in the cart completes successfully
    onNoteUpdateComplete? () => void Invoked when the process to add or update a note in the cart completes successfully
    onBuyerIdentityUpdateComplete? () => void Invoked when the process to update the buyer identity completes successfully
    onAttributesUpdateComplete? () => void Invoked when the process to update the cart attributes completes successfully
    onDiscountCodesUpdateComplete? () => void Invoked when the process to update the cart discount codes completes successfully

Patch Changes

  • Requests to SF API will now provide a Custom-Storefront-Group-ID header. (#2215) by @uri

1.5.0

Minor Changes

  • Special thank you to @kcarra for adding new mocked Providers for making testing easier! (#2224) by @blittle

    1. Add ServerRequestProvider mock for testing server components:
    import useServerHook from './useServerHook.server'; // Server hook to test
    import {test, vi} from 'vitest';
    import {renderHook} from '@testing-library/react-hooks';
    import {ShopifyProvider} from '@shopify/hydrogen';
    import {MockedServerRequestProvider} from '@shopify/hydrogen/testing';
    
    describe('useServerHook', () => {
      test('mocked ServerRequest Context', () => {
        const wrapper = ({children}: {children: React.ReactElement}) => (
          <MockedServerRequestProvider>
            <ShopifyProvider shopifyConfig={mockShopifyConfig}>
              {children}
            </ShopifyProvider>
          </MockedServerRequestProvider>
        );
        const {result} = renderHook(() => useServerHook(), {wrapper});
        expect(result.current).toEqual({status: 'active'});
      });
    });
    1. Add ShopifyTestProviders mock for easier testing client components and using client components in other contexts, like Storybook:
    import {ComponentMeta, ComponentStory} from '@storybook/react';
    import React from 'react';
    import BoxCardUI from './BoxCard.ui';
    import {ShopifyTestProviders} from '@shopify/hydrogen/testing';
    
    export default {
      title: 'Components/BoxCard',
      component: BoxCardUI,
      decorators: [],
    } as ComponentMeta<typeof BoxCardUI>;
    
    const Template: ComponentStory<typeof BoxCardUI> = (args) => {
      return (
        <ShopifyTestProviders>
          <BoxCardUI {...args} /> // This component imports import{' '}
          {(Image, Link, Money)} from '@shopify/hydrogen'
        </ShopifyTestProviders>
      );
    };
    
    export const BoxCard = Template.bind({});
    BoxCard.args = mockShopifyProduct;
  • Updated the Storefront API version of Hydrogen to the 2022-10 release. (#2208) by @frehner

    This is a backwards-compatible change; if you are still on the 2022-07 version, you may stay on that version without any issues. However, it is still recommended that you upgrade to 2022-10 as soon as possible.

    For more information about the Storefront API, refer to:

Patch Changes

  • Experimental version of a new cart provider is ready for beta testing. (#2219) by @lordofthecactus

    CartProviderV2 fixes race conditions with our current cart provider. After beta, CartProviderV2 will become CartProvider requiring no code changes.

    To try this new cart provider:

    import {CartProviderV2} from '@shopify/hydrogen/experimental';
    

1.4.4

Patch Changes

  • Prevent unhandled errors when location.hash is not a valid element selector for scrolling. (#2209) by @potench
  • Changing the content of a CSS Module should now trigger HMR instead of a full page refresh when using global CSS mode. (#2175) by @frandiox
  • Fix imports from @shopify/hydrogen/experimental at build time. Previously, importing from this path would end up in unresolved client components. (#2198) by @frandiox

1.4.3

Patch Changes

  • Critical fix for the CartProvider to remove an error when Cookies are disabled by the browser. (#2190) by @blittle

1.4.2

Patch Changes

  • We changed the default logging behavior to include the overall request outcome, either ok or an error. This is necessary because a streamed request might start with a 200 HTTP response code, and during the process of stream rendering an error is encountered. (#2161) by @blittle

1.4.1

Patch Changes

1.4.0

Minor Changes

  • Add the useDelay hook for artificial delays when rendering server components. This is useful to debug timing issues or building suspense boundary fallback UI. See the useDelay documentation. (#2109) by @blittle

Patch Changes

  • We've exposed the private server-to-server Storefront API token in the Hydrogen config file. This private token is required when deploying to production, otherwise the requests to the storefront API will be rate-limited. This change will make it easier to configure Hydrogen when deploying to non-Oxygen environments. We'll also display a warning in production mode if this token is not defined. (#1998) by @blittle

    We've also added the storefrontId property to the config. This enables Hydrogen data to display properly in the Shopify admin analytics dashboard.

    Lastly, we've updated all Oxygen environment variables to a more consistent naming convention. The previous variables are still available, but are deprecated, and will be removed in the future. You’ll see a warning in your console if you use the old environment variables. You can update your variable references using this table:

    Old Oxygen variable New Oxygen variable
    SHOPIFY_STORE_DOMAIN PUBLIC_STORE_DOMAIN
    SHOPIFY_STOREFRONT_API_PUBLIC_TOKEN PUBLIC_STOREFRONT_API_TOKEN
    SHOPIFY_STOREFRONT_API_SECRET_TOKEN PRIVATE_STOREFRONT_API_TOKEN
    SHOPIFY_STOREFRONT_ID PUBLIC_STOREFRONT_ID
  • Fixed dev-mode Hydrogen builds to properly show PUBLIC_ prefixed environment variables (#2142) by @blittle
  • An issue with previewing hydrogen in production has been fixed. Make sure that you upgrade both @shopify/cli and @shopify/cli-hydrogen to 3.12.0. (#2144) by @blittle

1.3.2

Patch Changes

  • Whenever using fetchSync, make sure to handle the error state. Though we've made changes to the error thrown by the JSON parser to also tell you that the request was unsuccessful: (#2070) by @blittle

    function MyComponent() {
      const response = fetchSync('/api');
    
      // Make sure the error state is handled!
      if (!response.ok) {
        console.error(
          `Unable to load ${response.url} returned ${response.status}`,
        );
        return <div>Error. Please try again</div>;
      }
    
      // Check `response.ok` before parsing the response
      const json = response.json();
    
      return ...
  • Added experimental support for Vite 3. By default, Hydrogen will still use Vite 2. However, it is possible to upgrade apps to Vite 3 by changing devDependencies in the app package.json. Beware that this is experimental and it might break. (#1992) by @frandiox
  • Hydrogen responses now contain a Link header to preload stylesheets. (#2075) by @frandiox
  • Improvements and fixes to hydrogen logging: (#2084) by @blittle

    1. API Routes are now passed a reference to the logger bound to the current request:
    export async function api(request, {log}) {
      log.warn("Here's a warning!");
      return new Request('Hello World');
    }
    1. If you define a custom logging implementation within your Hydrogen config, we'll now warn you when your logging implementation itself errors.
  • When a route is rendering, if Hydrogen has already started streaming, it is invalid to call response.doNotStream(). Disabling streaming should always happen before any async operation in your route server component. This change fixes Hydrogen to warn if you try to disable streaming after the stream has already begun. (#2081) by @frandiox

1.3.1

Patch Changes

  • <ExternalVideo/> now has a default prop of loading="lazy" to improve performance of the rendered <iframe>. (#2044) by @frehner

    If you're using <ExternalVideo/> above the fold, then we recommend setting this prop to eager.

  • Improve error handling: (#2049) by @blittle

    1. Improve how errors are default presented in the logs.
    2. Make sure that when useShopQuery fails, that an Error object is propagated.

    If you have implemented your own logging handler, it is recommended that you only print strings, as printing objects (including Error objects) will result in unhelpful logs in many runtimes (Oxygen included):

    // Example custom logging for errors
    export default defineConfig({
      logger: {
        error: (context, error) => {
          const url = context ? ` ${context.url}` : '';
    
          if (error instanceof Error) {
            // Do NOT directly just print the error, instead
            // print the error.messag or error.stack
            console.error(`Error:${url}\n${error.stack}`);
          } else {
            console.error(`Error:${url} ${error}`);
          }
        },
      },
    });

1.3.0

Minor Changes

  • Add the experimental useFlashSession hook. This hook reads and clears a session value. It is useful for request validation within the experimental <Form> component: (#1878) by @blittle

    import {Form, useFlashSession} from '@shopify/hydrogen/experimental';
    
    export default function Login() {
      const loginError = useFlashSession('loginError');
    
      return (
        <Form action="/login">
          {loginError ? <div>Invalid user!</div> : null}
          <input type="text" name="username" />
          <input type="password" name="password" />
          <button type="submit">Login</button>
        </Form>
      );
    }
    
    export async function api(request, {session}) {
      const data = await request.formData();
      const username = data.get('username');
      const password = data.get('password');
    
      const userId = await getUser(username, password);
    
      if (!userId) {
        await session.set('loginError', 'INVALID_USER');
        return new Request('/login');
      } else {
        await session.set('userId', userId);
        return new Request('/account');
      }
    }

    Note, useFlashSession is experimental, and subject to change at any time.

Patch Changes

  • Pass root to all Vite instances to support building projects from different directories. (#1987) by @frandiox
  • Added a new option assetHashVersion to the Hydrogen plugin in vite.config.js. This option can be used to manually change the assets file hash. (#2000) by @frandiox

    // vite.config.js
    export default {
      plugins: [hydrogen({assetHashVersion: 'v2'})],
    };
  • Fix accessing Oxygen.env in hydrogen.config.js file in production. (#1977) by @frandiox
  • <Image/> now sets the attribute decoding='async' by default, to potentially improve performance. (#1969) by @sanjaiyan-dev
  • Fix stale while revalidate when custom cache value is supplied (#1967) by @wizardlyhel
  • The Typescript types for the <Image/> component are now available to import directly from Hydrogen. (#1913) by @frehner

    import {
      type ShopifyImageProps,
      type ExternalImageProps,
    } from '@shopify/hydrogen';
  • Change how the RSC plugin communicates with other plugins to support vanilla-extract. (#1944) by @frandiox
  • Fix __rsc requests to have a cache-control header (#2010) by @blittle

1.2.0

Minor Changes

  • CartLineQuantityAdjustButton, BuyNowButton, and AddToCartButton now have an as property. The types for these components have also been improved. (#1827) by @blittle
  • Added a new experimental CSS approach to support importing styles directly in React Server Components. This feature must be enabled manually. (#1843) by @frandiox

    Until now, we had experimental support for CSS Modules with some minor restrictions and drawbacks:

    • Only server components that were the default export had access to the CSS Module automatically (i.e. it required extra work for named exports).
    • CSS Module was duplicated when used in multiple components.
    • RSC responses had all the CSS inlined, making them much larger.

    The new CSS approach adds full support for CSS Modules without the previous restrictions and drawbacks.

    Aside from that, it also adds support for pure CSS and enables a way to integrate with tools that provide CSS-in-JS at build time. All the CSS imported in both client and server components will be extracted in a CSS file at build time and downloaded with a <link rel="stylesheet"> tag. During development, styles will be inlined in the DOM to better support HMR.

    To activate this new experimental feature, pass the experimental.css: 'global' option to Hydrogen's Vite plugin:

    // vite.config.js
    export default {
      plugins: [hydrogen({experimental: {css: 'global'}})],
    };

    Examples:

    // App.server.jsx using pure CSS with global classes
    import './my-red-style.css';
    
    function App() {
      return <div className="red">...</div>;
    }
    // App.server.jsx using CSS Modules with scoped classes
    import {red} from './my-red.module.css';
    
    function App() {
      return <div className={red}>...</div>;
    }
  • Add support for custom 500 error pages. If an unexpected error occurs while rendering a route, Hydrogen will respond with a 500 HTTP error and render a default error page. Define a custom error page with the serverErrorPage configuration property: (#1867) by @blittle

    import {defineConfig} from '@shopify/hydrogen/config';
    
    export default defineConfig({
      ...
      serverErrorPage: '/src/Error.jsx',
    });

    The serverErrorPage property defaults to /src/Error.{jsx,tsx}. The custom error page is passed an Error property:

    export default function Error({error}) {
      return (
        <div>
          <h1>An unknown error occured!</h1>
          <h2>{error.message}</h2>
          <h3>{error.stack}</h3>
        </div>
      );
    }

Patch Changes

  • locale calculation logic and docs have been updated to support Shopify languages with extended language subtags. (#1836) by @lordofthecactus

    The following is how we calculate locale: If the Shopify language includes a region, then this value is used to calculate the locale and countryCode is disregarded. For example, if language is PT_BR (Brazilian Portuguese), then locale is calculated as PT-BR. If the Shopify language doesn't include a region, then this value is merged with the countryCode to calculate the locale. For example, if language is EN (English) and countryCode is US (United States), then locale is calculated as EN-US.

  • Added optional sellingPlanId prop to AddToCartButton.client.tsx (#1821) by @ChrisKG32
  • Fix: Hydrogen no longer caches error responses. Any 400 or 500 level response will not have a cache control-header, nor will Hydrogen cache it internally. (#1873) by @blittle
  • Allow sourceProps to be passed to <Video/>'s underlying <source> elements. (#1847) by @frehner

Special Thanks

  • Thanks to @mrkldshv for help in migrating tests from Jest to Vitest.
  • Thanks to @davidhousedev for constant feedback and discussions.

1.1.0

Migration for Stores based on the "Demo Store" template

If your Store is based on the "Demo Store" tempate, and you are using the test:ci NPM script, then you need to replace the contents of your /tests/utils.js or /tests/utils.ts file with the following:

  • For Typescript projects, replace /tests/utils.ts with the content found here
  • For Javascript projects, replace /tests/utils.js with the content found here

Minor Changes

  • Replace graphiql with graphql/graphql-playground in local development at '/graphql` route. (#1710) by @cartogram
  • Expose utilities for integrating Hydrogen with 3rd party platforms in @shopify/hydrogen/platforms. These utilities can resolve the project build path automatically and also find the client build assets. (#1772) by @frandiox

    import {
      handleRequest, // Instead of './src/App.server'
      indexTemplate, // Instead of './dist/client/index.html?raw'
      isAsset, // Access a list of files in './dist/client/**/*'
    } from '@shopify/hydrogen/platforms';
    
    // Platform entry handler
    export default function(request) {
      if (isAsset(new URL(request.url).pathname)) {
        return platformAssetHandler(request);
      }
    
      return handleRequest(request, {indexTemplate});
    }

    Note that user apps don't need to be changed.

Patch Changes

  • Serve assets in public directory from the same origin when deploying to Oxygen. (#1815) by @frandiox

    Normally, public assets are served from a CDN domain that is different from the storefront URL. This creates issues in some situations where the assets need to be served from the same origin, such as when using service workers in PWA or tools like Partytown. This change adds a proxy so that the browser can download assets from the same origin.

    Note that, for performance reasons, it is still recommended placing assets in /src/assets instead of /public whenever possible.

  • The payload returned by fetchSync was supposed to mimic react-fetch but it wrongly moved the Response data to a sub-property response. This has been fixed to have the Response at the top level. Also, cached responses are now correctly serialized and retrieved to avoid issues on cache hit. (#1760) by @frandiox

    const response = fetchSync('...');
    -response.response.headers.get('...');
    +response.headers.get('...');
    const jsonData = response.json();

    Note that the sub-property response is still available but marked as deprecated.

  • Improve error messaging when there is an error in the Storefront API's GraphQL response. (#1837) by @frehner
  • Fix server props when using non UTF-8 characters in URL. (#1780) by @frandiox
  • Fix HMR in client components. It should now update only the modified client component in the browser instead of refreshing the entire page. (#1818) by @frandiox

1.0.2

Patch Changes

  • <Image/> component has improved handling for width as a string. It also corrects an issue with the scale prop and its usage with the Shopify CDN. The generated srcset is also updated and improved. (#1723) by @frehner
  • We've decided to deprecate the <LocalizationProvider>, and instead put all its functionality into <ShopifyProvider>. The justification is that both providers are required by many components and hooks, and we think it's easier to have a single required <ShopifyProvider> instead of two. The same props available to the <LocalizationProvider> are now available on the <ShopifyProvider>. (#1735) by @blittle

    // App.server.tsx
    function App({routes, request}: HydrogenRouteProps) {
      ...
      return (
        <Suspense fallback={<HeaderFallback isHome={isHome} />}>
    +     <ShopifyProvider countryCode={countryCode as CountryCode}>
    -     <ShopifyProvider>
    -     <LocalizationProvider countryCode={countryCode as CountryCode}>
            <CartProvider countryCode={countryCode as CountryCode}>
                ...
            </CartProvider>
            <PerformanceMetrics />
            {import.meta.env.DEV && <PerformanceMetricsDebug />}
            <ShopifyAnalytics />
    -     </LocalizationProvider>
          </ShopifyProvider>
        </Suspense>
      );
    }

    Note: this is not a breaking change. <LocalizationProvider> will still be available, but all documentation will now point to <ShopifyProvider>.

  • Remove formData polyfill in worker environments. (#1740) by @frandiox
  • Fix useProductOptions export to avoid errors at build time. (#1738) by @frandiox

1.0.1

Patch Changes

1.0.0

Major Changes

  • #1700 9b6c564e Thanks @jplhomer! - Hydrogen is now out of developer preview. Thank you for all of your feedback and contributions the past eight months!

0.27.0

Minor Changes

  • #1697 85aab092 Thanks @blittle! - Remove defaultLocale from the Hydrogen Config and instead add defaultCountryCode and defaultLanguageCode. Both of which are also now available by the useShop() hook:

    export default defineConfig({
      shopify: {
    -    defaultLocale: 'EN-US',
    +    defaultCountryCode: 'US',
    +    defaultLanguageCode: 'EN',
        storeDomain: 'hydrogen-preview.myshopify.com',
        storefrontToken: '3b580e70970c4528da70c98e097c2fa0',
        storefrontApiVersion: '2022-07',
      },
    }
  • #1662 4262b319 Thanks @wizardlyhel! - Fix server analytics route

    • Fix ServerAnalyticsRoute so that it does complete all async work
    • Move Performance and Shopify analytic reporting to client side
    • Make sure ShopifyAnalytics make its own query for shop id and currency
    • Remove query for shop id and currency from DefaultSeo component
    • Make Performance and Shopify server analytics connector do nothing

    Deprecated components

    Remove the following components from hydrogen.config.js

    • PerformanceMetricsServerAnalyticsConnector
    • ShopifyServerAnalyticsConnector

0.26.1

Patch Changes

  • #1680 acf5223f Thanks @blittle! - Fix basepath to not apply to external URLs in the <Link component. Also default the attribute rel="noreferrer noopener for external URLs.

0.26.0

Minor Changes

  • #1615 20bfc438 Thanks @frehner! - <CartEstimatedCost/> has been renamed to <CartCost/> to match a recent update to the Storefront API, in which cart.estimatedCost is being deprecated in favor of cart.cost.

    Additionally, cart.cost.compareAtAmount was renamed to cart.cost.compareAtAmountPerQuantity.

  • #1619 b0c13696 Thanks @blittle! - We have reworked how localization works in Hydrogen. By default the useLocalization() hook returns the default locale defined within your Hydrogen configuration file. The <LocalizationProvider> component now takes countryCode and languageCode as optional props. Any props given to <LocalizationProvider> will also be used by the useLocalization hook.

    Breaking Change

    The useCountry hook has been removed. Instead use the useLocalization hook.

    - import {useCountry, gql} from '@shopify/hydrogen';
    + import {useLocalization, gql} from '@shopify/hydrogen';
    
    export function MyComponent() {
    
    -  const [country] = useCountry();
    +  const {country} = useLocalization();
    
      return ( /* Your JSX */ );
    }

    The Link component now respects the basePath property defined within it's parent FileRoutes component. For example, given <FileRoutes basePath="/cn">, a route within that renders <Link to="/products"> will actually produce an anchor tag prefixed with /cn: <a href="/cn/products">. You can override the basePath with a basePath prop on the Link component.

Patch Changes

  • #1613 c45ebd3c Thanks @frehner! - The <ShopPayButton/> and <CartShopPayButton/> now take in a width prop to help customize how wide the <shop-pay-button> custom element is, by using the newly added CSS custom property (variable) --shop-pay-button-width.
  • #1651 a19be2b2 Thanks @blittle! - Fixes to the cart:

    1. Fix bug when providing a lower-case country code to the LocalizationProvider
    2. Make sure that the Cart always logs API errors
  • #1649 df0e01ff Thanks @blittle! - Add a x-powered-by: Shopify-Hydrogen header which can be disabled with the Hydrogen config property: poweredByHeader: false
  • #1551 3d20e92d Thanks @jplhomer! - In-Memory caching is now enabled by default in production for Node.js runtimes.
  • #1604 f3827d31 Thanks @cartogram! - Adds new load-config entry point that exposes a loadConfig() function that will return the configuration object and the path to the found configuration file for a given Hydrogen project root.

    Example:

    import {loadConfig} from '@shopify/hydrogen/load-config';
    
    const {configuration, configurationPath} = await loadConfig({
      root: 'path/to/hydrogen-app',
    });
  • #1608 b834dfdc Thanks @jplhomer! - Add type exports for HydrogenRouteProps, HydrogenApiRoute, and HydrogenApiRouteOptions.

0.25.1

0.25.0

Minor Changes

  • #1570 36f26e54 Thanks @frehner! - <Image/> now takes into account a specific order for determining the width and height.

    1. loaderOptions's width/height
    2. width/height bare props
    3. data's width/height

    getShopifyImageDimensions() was also updated to handle this logic.

  • #1506 58d6ef55 Thanks @wizardlyhel! - Hydrogen now supports full-page caching out of the box. Previously, Hydrogen relied on the network edge to provide full-page caching for dynamic responses (HTML).
  • #1523 4ef2e5b9 Thanks @blittle! - We've simplified the built-in Hydrogen caching strategies. Instead of CacheSeconds, CacheMinutes, CacheHours, CacheDays, CacheMonths, and NoStore, there is no simply CacheLong, CacheShort, and CacheNone. Please remember that you can build your own caching strategies.
  • #1513 8d67b559 Thanks @frandiox! - Breaking change: We are starting to use exports property in package.json to list all the entry points in this package.

    This might incur breaking changes in some rare cases when importing private properties from Hydrogen dist files. Notice that we discourage doing so for anything that is not publicly documented but, if your app was relying on some private properties, then this might help:

    -import {xyz} from '@shopify/hydrogen/dist/esnext/<internal-path>';
    +import {xyz} from '@shopify/hydrogen/<internal-path>';

    Aside from that, it is recommended that TypeScript projects update the tsconfig.json file to use compilerOptions.moduleResolution: "node16" to make sure Hydrogen types are loaded in your editor. For JavaScript projects, create or edit <root>/jsconfig.json file with the following information to improve typings:

    {
      "compilerOptions": {
        "target": "es2020",
        "module": "esnext",
        "moduleResolution": "node16",
        "lib": ["dom", "dom.iterable", "scripthost", "es2020"],
        "jsx": "react",
        "types": ["vite/client"]
      },
      "exclude": ["node_modules", "dist"],
      "include": ["**/*.js", "**/*.jsx"]
    }
  • #1528 72d21b87 Thanks @frehner! - Metafields have changed in Storefront API 2022-07. We updated our code to work with that update, which means that the following changes will only work if you're using 2022-07 or newer.

    Metafields changes

    Storefront API 2022-07

    Metafields have changed how you access them in the Storefront API. See the release notes for more details. In order to support the new way of querying metafields, Hydrogen has made the following updates:

    <Metafield/>

    Previously, the <Metafield/> component expected you to use useParseMetafields() before passing a metafield to it.

    Now, <Metafield/> will use parseMetafield() itself so that you don't have to. However, this does mean that if you use parseMetafield() and then pass it to <Metafield/>, it will likely break because it will try to parse your metafield's value a second time.

    useParsedMetafields() and parseMetafield()

    Deprecated useParsedMetafields() in favor of parseMetafield(). parseMetafield() takes in a single metafield and returns a new object, and importantly it can be used on both the client and the server.

    If you need to memoize the value on the client, then you can do so using React.memo:

    import {useMemo} from 'react';
    import {parseMetafield} from '@shopify/hydrogen'x
    
    function MyComponent() {
      const parsedMetafield = useMemo(() => parseMetafield(metafield), [metafield]);
    }
  • #1517 68b8185e Thanks @frandiox! - Breaking change: The utilities used in hydrogen.config.js file are now exported from @shopiy/hydrogen/config instead of @shopify/hydrogen:

    -import {defineConfig} from '@shopify/hydrogen/config';
    import {
    + defineConfig,
      CookieSessionStorage,
      PerformanceMetricsServerAnalyticsConnector,
      ShopifyServerAnalyticsConnector,
    -} from '@shopify/hydrogen';
    +} from '@shopify/hydrogen/config'
    
    export default defineConfig({
      shopify: {/* ... */},
      session: CookieSessionStorage('__session', {
        path: '/',
        /* ... */
      }),
      serverAnalyticsConnectors: [
        PerformanceMetricsServerAnalyticsConnector,
        ShopifyServerAnalyticsConnector,
      ],
    });

Patch Changes

  • #1593 ae35b70b Thanks @juanpprieto! - Ensure the effect that updates the cart.buyerIdenity.countryCode is run when countyCode prop changes
  • #1375 217b5f23 Thanks @blittle! - Add a built-in healthcheck route available at /__health. It responds with a 200 and no body. Also suppresses server logs for built-in routes like healthcheck and analytics.
  • #1497 3364225f Thanks @blittle! - Improve waterfall detection

    1. Show a summary in dev mode with instructions on getting details
    2. Only show the waterfall warning the second time the page is loaded
    3. Don't show the waterfall warning on preloaded queries
  • #1571 accdc78a Thanks @jplhomer! - Upgrade Hydrogen to React v18.2. To update your app, run yarn add @shopify/hydrogen@latest react@latest react-dom@latest.
  • #1578 f5290393 Thanks @frandiox! - Fix an issue where newly imported client components were not found in the browser.
  • #1556 06f3d174 Thanks @blittle! - Add support for Request.formData() within API Routes for Node 16-17. Example:

    export async function api(request) {
      const formData = await request.formData();
    
      const username = formData.get('user');
      const password = formData.get('pass');
    
      ...
    }

0.24.0

Minor Changes

  • #1489 e2ee2d45 Thanks @blittle! - In an effort to be performant by default, the preloaded queries are turned on by default when caching is also enabled. By default, each query has caching enabled too, so preload will on universally by default.
  • #1237 356e75f3 Thanks @frehner! - Updated Cart queries in two ways, one of which requires you to be using Storefront API 2022-07:

    1. CartLine now uses CartLineEstimatedCost's totalAmount field for calculating the Line's total, instead of doing it manually.
    2. Cart now uses totalQuantity for calculating how many items are in the cart, instead of doing this manually. Note that this feature is only available in Storefront API 2022-07 and newer.

Patch Changes

  • #1509 05081b01 Thanks @jplhomer! - Fix <BuyNowButton> so it can be rendered without being nested in a <CartProvider>
  • #1471 5b4e08df Thanks @frandiox! - Added an experimental hook useRequestContext that provides server-only context for third party integrations.
  • #1486 a31e007d Thanks @frehner! - Fix <ProductOptionsProvider/>'s setSelectedOptions() function to update the selectedVariant as well

0.23.0

Minor Changes

  • #1389 9a21108f Thanks @blittle! - Breaking change

    The utility isClient has been renamed to isBrowser. This is because the utility really checks if the running context is a browser, not if the context is a client component.

    All client components by default also run on the server when they are server rendered. If you don't want that to happen, use the isBrowser() hook. Remember that anything not server rendered will be unavailable for SEO bots.

  • #1431 6975bdb9 Thanks @jplhomer! - Add scroll prop to Link and navigate to allow the scroll restoration behavior to be disabled.

    By default, when a <Link> component is clicked, Hydrogen emulates default browser behavior and attempts to restore the scroll position previously used in the visitor's session. For new pages, this defaults to scrolling to the top of the page.

    However, if you are building a user interface that should fetch a new server components request and update the URL but not modify scroll position, then you can disable scroll restoration using the scroll prop:

    import {Link} from '@shopify/hydrogen';
    export default function Index({request}) {
      const url = new URL(request.normalizedUrl);
    
      return (
        <>
          <p>Current param is: {url.searchParams.get('param')}</p>
          <Link to="/?param=foo" scroll={false}>
            Update param to foo
          </Link>
        </>
      );
    }
  • #1325 572c18d1 Thanks @wizardlyhel! - - Fix clientAnalytics not waiting for all server analytics data before sending page view event

    • Fix server analytics connector erroring out after more than 1 server analytics connectors are attached
    • Shopify analytics components

    Updates to server analytics connectors

    The server analytics connector interface has updated to

    export function request(
      requestUrl: string,
      requestHeader: Headers,
      data?: any,
      contentType?: string
    ): void {
      // Do something with the analytic event.
    }

    Introducing Shopify analytics

    Optional analytics components that allows you to send ecommerce related analytics to Shopify. Adding the Shopify analytics components will allow the Shopify admin - Analytics dashboard to work.

    For information, see Shopify Analytics

  • #1334 58e039d4 Thanks @blittle! - With the introduction of authenticated pages, we also now provide the ability to prevent pages from being indexed by bots. You can do so by passing noindex to the Seo component:

    <Seo type="noindex" data={{title: 'Login'}} />
  • #1397 fbd185ab Thanks @frehner! - ## <ProductProvider/> and <ProductOptionsProvider/>

    • <ProductProvider/> has been removed
      • <ProductPrice/> was the only component left that used it; now it requires a data prop that takes in the product object
    • <ProductOptionsProvider/> now maintains and provides the state that useProductOptions used to keep track of by itself. This change enables you to use multiple useProductOptions hook calls and have them share the same state (such as selected variant, options, etc.)
  • #1403 979f8177 Thanks @frandiox! - Breaking change: The setLogger and setLoggerOptions utilities have been removed. The same information can now be passed under the logger property in Hydrogen config:

    // App.server.jsx
    
    -import {setLogger, setLoggerOptions} from '@shopify/hydrogen';
    
    -setLogger({
    -  trace() {},
    -  error() {},
    -  // ...
    -});
    
    -setLoggerOptions({
    -  showQueryTiming: true,
    -  showCacheControlHeader: true,
    -  // ...
    -});
    
    function App() {
      // ...
    }
    
    export default renderHydrogen(App);
    // hydrogen.config.js
    
    export default defineConfig({
      // ...
    + logger: {
    +   trace() {},
    +   error() {},
    +   showQueryTiming: true,
    +   showCacheControlHeader: true,
    +   // ...
    + },
    });
  • #1433 cd354d3a Thanks @frandiox! - The response.writeHead method has been removed, while response.status and response.statusText are now writable.

    function App({response}) {
    - response.writeHead({
    -   headers: {'custom-header': 'value'},
    -   status: 404,
    - });
    + response.headers.set('custom-header', 'value');
    + response.status = 404;
    }
  • #1418 512cb009 Thanks @frandiox! - Breaking change: The client configuration, including the strictMode option, has been moved from custom client entry handlers to the Hydrogen configuration file. If you had a custom client entry file just to pass client options, you can remove it and do the same in hydrogen.config.js:

    // Custom client entry handler
    
    -renderHydrogen(ClientWrapper, {strictMode: false});
    +renderHydrogen(ClientWrapper);
    // hydrogen.config.jsx
    
    export default defineConfig({
    +  strictMode: false,
    });

    To remove a custom client entry handler in case it's not needed anymore, delete the custom file and change index.html:

    <body>
      <div id="root"></div>
    - <script type="module" src="/src/custom-client-entry"></script>
    + <script type="module" src="/@shopify/hydrogen/entry-client"></script>
    </body>
  • #1401 335b70ce Thanks @frandiox! - Breaking change: The enableStreaming config option has been deprecated. The same feature can be done directly in the app:

    // hydrogen.config.js
    
    export default defineConfig({
      shopify: {
        // ...
      },
    - enableStreaming: (req) => {
    -   return req.headers.get('user-agent') !== 'custom bot';
    - },
    });
    // App.server.jsx
    
    -function App() {
    +function App({request, response}) {
    + if (request.headers.get('user-agent') === 'custom bot') {
    +   response.doNotStream();
    + }
    
      return <Suspense fallback={'Loading...'}>{/*...*/}</Suspense>;
    }
    
    export default renderHydrogen(App);

Patch Changes

  • #1425 e213aa86 Thanks @frandiox! - Rename internal Hydrogen global variables that could conflict with third party libraries that use the same names.
  • #1361 cf2ef664 Thanks @frandiox! - Improve component bundling to reduce the total amount of JS files downloaded in the browser.
  • #1444 0b4ee487 Thanks @blittle! - Propagate a better error message when the response from the storefront API is not JSON parseable
  • #1424 446c12bf Thanks @frandiox! - Custom loggers can return promises from their methods. Hydrogen will await for them after the current request is over but before the runtime instance ends.
  • #1423 aaf9efa4 Thanks @frandiox! - Workers context (e.g. waitUntil) is now scoped to the current request instead of globally available.
  • #1330 c7dc6440 Thanks @ejfranco06! - [#1245] - Generate a default srcset for an image returned by the Shopify CDN on the Image component and allow using a custom set of widths.

0.22.1

Patch Changes

0.22.0

Minor Changes

  • #930 750baf8f Thanks @michenly! - With the introduction of authenticated pages, we also now provide the ability to prevent pages from being indexed by bots. You can do so by passing noindex to the Seo component:

    <Seo type="noindex" data={{title: 'Login'}} />
  • #1313 ed1933e3 Thanks @frandiox! - Breaking change: The routes property in hydrogen.config.js file has been simplified. It is now a string that represents the path to the routes from the project root:

    // hydrogen.config.js
    
    export default defineConfig({
    -  routes: import('./src/routes/**/*.server.[jt](s|sx)'),
    +  routes: '/src/routes',
    });

    Its default value is /src/routes so this property can be removed when using this directory.

    In the object syntax version, dirPrefix is removed and files becomes a string:

    // hydrogen.config.js
    
    export default defineConfig({
      routes: {
    -   files: import('./src/routes/**/*.server.[jt](s|sx)'),
    -   dirPrefix: './src/routes',
    +   files: '/src/routes',
        basePath: '/',
      },
    });
  • #1332 5ec1bc62 Thanks @frandiox! - A new gql utility is exported from @shopify/hydrogen that replaces graphql-tag dependency when using useShopQuery. It helps reducing bundle size in production when compared to the original graphql-tag.

    Before:

    import gql from 'graphql-tag';
    
    // ...
    
    useShopQuery({
      query: gql`...`,
      // ...
    });

    After:

    import {gql} from '@shopify/hydrogen';
    
    // ...
    
    useShopQuery({
      query: gql`...`,
      // ...
    });
  • #1340 631832ec Thanks @jplhomer! - Breaking change: The response.send() function has been removed. Use export async function api() to send custom responses instead.

Patch Changes

  • #1371 84a2fd09 Thanks @frehner! - Made updates to <Image/>:

    • Fixed some TypeScript type issues with Image.
    • data.url and alt are now required props in Typescript, but won't break the actual component if you don't pass them.
  • #1322 36bd77c4 Thanks @frandiox! - Fix server hanging in Node.js environment when not using Hydrogen Middleware.
  • #1360 d9b0d03b Thanks @blittle! - Fix a problem where encoded html content props passed from server to client components would get double decoded, and break hydration on app load.
  • #1339 fef4cb84 Thanks @jplhomer! - Use import.meta.env.DEV instead of process.env.LOCAL_DEV to hash asset filenames and show performance metrics debugging

0.21.0

Minor Changes

  • #1327 ce56311f Thanks @frehner! - Breaking Change: <Money /> updates and <UnitPrice /> Removed.

    • <UnitPrice/> has been removed
    • <Money/> has two new props: measurement and measurementSeparator which do the work that UnitPrice used to do
    • The TypeScript types for <Money/> have been improved and should provide a better typed experience now
  • #1304 aa196150 Thanks @frehner! - Removed <ProductTitle/> and <ProductDescription/> components. To migrate, use {product.title} and {product.description} instead.
  • #1335 0d90f92b Thanks @blittle! - Breaking Change

    The <ProductMetafield /> component has been removed. Instead, directly use the <Metafield> component.

Patch Changes

  • #1311 3e3fd72f Thanks @jplhomer! - Client components no longer need to use @shopify/hydrogen/client as the import path. All Hydrogen components can now be imported from @shopify/hydrogen regardless of their context.
  • #1259 110e9aca Thanks @blittle! - You can now easily disable streaming on any page conditionally with the enableStreaming option inside hydrogen.config.js:

    import {CookieSessionStorage} from '@shopify/hydrogen';
    import {defineConfig} from '@shopify/hydrogen/config';
    
    export default defineConfig({
      routes: import.meta.globEager('./src/routes/**/*.server.[jt](s|sx)'),
      shopify: {
        defaultLocale: 'en-us',
        storeDomain: 'hydrogen-preview.myshopify.com',
        storefrontToken: '3b580e70970c4528da70c98e097c2fa0',
        storefrontApiVersion: '2022-07',
      },
      enableStreaming: (req) => req.headers.get('user-agent') !== 'custom bot',
    });

    By default all pages are stream rendered except for SEO bots. There shouldn't be many reasons to disable streaming, unless there is a custom bot not covered by Hydrogen's bot detection.

  • #1318 668a24da Thanks @blittle! - Buffer RSC flight responses. There isn't any benefit to streaming them, because we start a transition on page navigation. Buffering also fixes caching problems on the flight response.
  • #1283 eea82cb0 Thanks @jplhomer! - Hydrogen has been updated to use the latest stable version of React.

    To update an existing Hydrogen app:

    yarn add react@latest react-dom@latest

0.20.0

Minor Changes

  • #1257 5cd7a672 Thanks @frandiox! - Support for CSS Modules has been improved. It now behaves closer to the default behavior in Vite where styles are collected automatically.

    Remove the StyleTag component that was needed before:

    export default function MyComponent() {
      return (
        <div>
    -      <myStyles.StyleTag />
          <h1>Title</h1>
        </div>
      );
    }

    Optionally, update your wildcard imports to default or named imports:

    -import * as myStyles from './my.module.css';
    +import myStyles from './my.module.css';
    // Or
    +import {red, green, blue} from './my.module.css';
  • #1271 9d0359b8 Thanks @frehner! - ## <Image/>

    The <Image/> component and related utility functions were reworked and the following changes apply:

    • useImageUrl is no longer available; use shopifyImageLoader instead, which is available to run both server- and client-side.
    • The TypeScript experience with <Image/> is improved; props will be validated better, and loader and loaderOptions will be better typed
    • When using the src prop, width and height are now required
    • When using the data prop, data.width and data.height or width and height props are required
    • The src and data props are mutually exclusive
    • The loader prop now receives a singular param as an object
    • options has been merged with loaderOptions. When using the data prop, loaderOptions will be the options for Shopify CDN images. When using the src prop, loaderOptions will be whatever you define them to be.
    • The TypeScript type ImageSizeOptions is now named ShopifyLoaderOptions
    • The TypeScript type ImageLoaderOptions is now named ShopifyLoaderParams
    • The priority prop was removed; use the HTML-standard loading prop instead

    <Video/>

    • The <Video/> component's options props was renamed to imagePreviewOptions to add clarity as to what the options were for.
    • imagePreviewOptions matches the (newly updated) shape of <Image/>'s loaderOptions

Patch Changes

  • #1247 ee64873e Thanks @frandiox! - Improve the way client components are discovered in order to reduce bundle sizes.

0.19.0

Minor Changes

  • #1053 c407f304 Thanks @blittle! - The selected country is now persisted a part of the session. This means that the page can be refreshed and the country will still be selected. There are a few breaking changes:

    1. useCountry() hook now only returns the currently selected country. The setCountry() method has been removed.
    2. The useCountry() hook expects a countryCode and countryName to be a part of the user session.
    3. The example /countries API route has been updated to accept a POST request to update the selected country. The CountrySelector components need to be updated to use that route.
    // src/routes/countries.server.jsx
    
    -export async function api(request, {queryShop}) {
    +export async function api(request, {queryShop, session}) {
    +  if (request.method === 'POST') {
    +    const {isoCode, name} = await request.json();
    +
    +    await session.set('countryCode', isoCode);
    +    await session.set('countryName', name);
    +
    +    return 'success';
    +  }
    
       const {
         data: {
           localization: {availableCountries},
         },
       } = await queryShop({
          query: QUERY,
       });
       return availableCountries.sort((a, b) => a.name.localeCompare(b.name));
    }
    // src/components/CountrySelector.client.jsx
    
    export default function CountrySelector() {
      const [listboxOpen, setListboxOpen] = useState(false);
    
    - const [selectedCountry, setSelectedCountry] = useCountry();
    + const [selectedCountry] = useCountry();
    
    + const setSelectedCountry = useCallback(
    +   ({isoCode, name}) => {
    +     fetch(`/countries`, {
    +       body: JSON.stringify({isoCode, name}),
    +       method: 'POST',
    +     })
    +       .then(() => {
    +         window.location.reload();
    +       })
    +       .catch((error) => {
    +         console.error(error);
    +       });
    +   },
    +   [],
    + );
    
      return (
          ...
      );
    }
    1. Each server component page that depends on the selected country pulls it from the session with useSession(), rather than serverProps.
    // src/routes/products/[handle].server.jsx
    + import { useSession } from '@shopify/hydrogen';
    
    - export default function Product({country = {isoCode: 'US'}}) {
    + export default function Product() {
        const {handle} = useRouteParams();
    +   const {countryCode = 'US'} = useSession();
        ...
      }

Patch Changes

  • #1245 07866e82 Thanks @0x15f! - #1245 - Support optional priority prop on Image component. When true, the image will be eagerly loaded. Defaults to false.

0.18.0

Minor Changes

  • #1065 81ae47fd Thanks @frandiox! - A new config file hydrogen.config.js replaces the existing shopify.config.js in your Hydrogen app.

    Introducing hydrogen.config.js

    Hydrogen apps now expect a hydrogen.config.js in the root folder. This config file accepts Shopify storefront credentials, routes, session configuration, and more.

    To migrate existing apps, you should create a hydrogen.config.js (or hydrogen.config.ts) file in your Hydrogen app:

    import {defineConfig} from '@shopify/hydrogen/config';
    import {
      CookieSessionStorage,
      PerformanceMetricsServerAnalyticsConnector,
    } from '@shopify/hydrogen';
    
    export default defineConfig({
      routes: import.meta.globEager('./src/routes/**/*.server.[jt](s|sx)'),
      shopify: {
        storeDomain: 'YOUR_STORE.myshopify.com',
        storefrontToken: 'YOUR_STOREFRONT_TOKEN',
        storefrontApiVersion: '2022-07',
      },
      session: CookieSessionStorage('__session', {
        path: '/',
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 60 * 60 * 24 * 30,
      }),
      serverAnalyticsConnectors: [PerformanceMetricsServerAnalyticsConnector],
    });

    Then, update your App.server.jsx to remove previous arguments from renderHydrogen():

    import renderHydrogen from '@shopify/hydrogen/entry-server';
    
    -function App({routes}) {
    +function App() {
      return (
        <Suspense fallback={<LoadingFallback />}>
    -      <ShopifyProvider shopifyConfig={shopifyConfig}>
    +      <ShopifyProvider>
            <CartProvider>
              <DefaultSeo />
              <Router>
    -            <FileRoutes routes={routes} />
    +            <FileRoutes />
                <Route path="*" page={<NotFound />} />
              </Router>
            </CartProvider>
            <PerformanceMetrics />
            {process.env.LOCAL_DEV && <PerformanceMetricsDebug />}
          </ShopifyProvider>
        </Suspense>
      );
    }
    
    -const routes = import.meta.globEager('./routes/**/*.server.[jt](s|sx)');
    -
    -export default renderHydrogen(App, {
    -  routes,
    -  shopifyConfig,
    -  session: CookieSessionStorage('__session', {
    -    path: '/',
    -    httpOnly: true,
    -    secure: process.env.NODE_ENV === 'production',
    -    sameSite: 'strict',
    -    maxAge: 60 * 60 * 24 * 30,
    -  }),
    -  serverAnalyticsConnectors: [PerformanceMetricsServerAnalyticsConnector],
    -});
    +export default renderHydrogen(App);

    Next, update vite.config.js in your app to remove references to shopifyConfig:

    import {defineConfig} from 'vite';
    import hydrogen from '@shopify/hydrogen/plugin';
    -import shopifyConfig from './shopify.config';
    
    // https://vitejs.dev/config/
    export default defineConfig({
    -  plugins: [hydrogen(shopifyConfig)],
    +  plugins: [hydrogen()],

    Finally, delete shopify.config.js from your app.

    Read more about the hydrogen.config.js file

Patch Changes

  • #1215 a0ed7c06 Thanks @frehner! - useMoney now returns two additional properties: withoutTrailingZeros and withoutTrailingZerosAndCurrency

    <Money /> now has two additional and optional props: withoutMoney and withoutCurrency.

0.17.3

Patch Changes

  • #1096 0a15376e Thanks @wizardlyhel! - Make performance data available with ClientAnalytics and optional for developers to include

0.17.2

Patch Changes

0.17.1

Patch Changes

0.17.0

Minor Changes

  • #1044 c8f5934d Thanks @blittle! - Hydrogen now has a built in session and cookie implementation. Read more about how sessions work in Hydrogen. The starter template also includes a cookie session storage implementation. To use the new session implementation within an existing Hydrogen app:

    import renderHydrogen from '@shopify/hydrogen/entry-server';
    import {
      Router,
      Route,
      FileRoutes,
      ShopifyProvider,
    +  CookieSessionStorage,
    } from '@shopify/hydrogen';
    import {Suspense} from 'react';
    import shopifyConfig from '../shopify.config';
    import DefaultSeo from './components/DefaultSeo.server';
    import NotFound from './components/NotFound.server';
    import LoadingFallback from './components/LoadingFallback';
    import CartProvider from './components/CartProvider.client';
    
    function App({routes}) {
      return (
        <Suspense fallback={<LoadingFallback />}>
          <ShopifyProvider shopifyConfig={shopifyConfig}>
            <CartProvider>
              <DefaultSeo />
              <Router>
                <FileRoutes routes={routes} />
                <Route path="*" page={<NotFound />} />
              </Router>
            </CartProvider>
          </ShopifyProvider>
        </Suspense>
      );
    }
    
    const routes = import.meta.globEager('./routes/**/*.server.[jt](s|sx)');
    
    export default renderHydrogen(App, {
      routes,
      shopifyConfig,
    +  session: CookieSessionStorage('__session', {
    +    path: '/',
    +    httpOnly: true,
    +    secure: process.env.NODE_ENV === 'production',
    +    sameSite: 'strict',
    +    maxAge: 60 * 60 * 24 * 30,
    +  }),
    });
    
  • #881 a31babfb Thanks @jplhomer! - ## Change from serverState to serverProps

    Breaking changes:

    1. useServerState() is gone. Use useServerProps() instead
    2. useServerProps() is reset on each page navigation. Previously useServerState() was not.
    3. useServerProps() does not contain pathname and search. Use the useNavigate hook to programmatically navigate instead.

    Explanation:

    The current behavior of server state is to persist indefinitely (until a hard page reload). This works great for things like the CountrySelector, where the updated state is meant to persist across navigations. This breaks down for many other use cases. Consider a collection paginator: if you paginate through to the second page of a collection using server state, visit a product page, and then go to a different collection page, the new collection page will use that same pagination variable in server state. This will result in a wonky or errored experience.

    Additionally, we have found that the term for serverState is confusing. The hook is used within client components, yet the state is passed as a prop to server components.

    As a result, serverState is now gone. Instead communicating between client and server components is through serverProps. If a client component wants to re-render server content, it just calls setServerProps('some', 'data'). Those props will be serialized to the server, and the server component will re-render. Additionally, the server props are reset on page navigation. So that they will not bleed between pages (fixes #331).

    If you previously relied on serverState for global state in your app, you shouldn't use serverProps anymore. Instead we'll introduce a new session based mechanism for global state (in the meantime you could manually manage a cookie).

    Lastly, serverProps no longer include the pathname and search parameters. Programmatically navigate in hydrogen instead with the useNavigate hook.

Patch Changes

  • #1131 8199023b Thanks @blittle! - Fix hydration issue where strings with $ would get converted to a single $
  • #1105 57ececf8 Thanks @frehner! - Regenerate the graphql.schema.json which should fix the sudden end-of-line termination, and makes the schema valid again.
  • #1099 6b50d371 Thanks @blittle! - Fix Hydrogen to not hard fail when client analytics doesn't load. Analytics might fail to load due to client-side adblockers.

0.16.1

Patch Changes

0.16.0

Patch Changes

  • #1082 bd14340c Thanks @jplhomer! - Update useUrl() to allow a developer to subscribe to a reactive version of the current router location.

    Example:

    import {useUrl} from '@shopify/hydrogen/client';
    
    function MyClientComponent() {
      const url = useUrl();
    
      useEffect(() => {
        // Record navigation, analytics, etc
      }, [url]);
    }

0.15.0

Minor Changes

  • #983 52af261b Thanks @jplhomer! - Introduce Suspense-friendly fetchSync API for server and client components.

    When using fetchSync in server components, you provide options for caching and preloading. This is similar to the useQuery hook:

    import {fetchSync, CacheMinutes} from '@shopify/hydrogen';
    import {Suspense} from 'react';
    
    export function MyServerComponent() {
      return (
        <Suspense fallback="Loading...">
          <MyThings />
        </Suspense>
      );
    }
    
    function MyThings() {
      const things = fetchSync('https://3p.api.com/things.json', {
        preload: true,
        cache: CacheMinutes(),
      }).json();
    
      return <h2>{things.title}</h2>;
    }

    When using fetchSync in client components, you cannot provide options for caching and preloading. You must import fetchSync from @shopify/hydrogen/client:

    import {fetchSync} from '@shopify/hydrogen/client';
    import {Suspense} from 'react';
    
    export function MyClientComponent() {
      return (
        <Suspense fallback="Loading...">
          <MyThings />
        </Suspense>
      );
    }
    
    function MyThings() {
      const things = fetchSync('https://3p.api.com/things.json').json();
    
      return <h2>{things.title}</h2>;
    }

Patch Changes

  • #1057 06d92ddc Thanks @frandiox! - Ability to concatenate requests in API route handlers without leaving the server by returning a new Request instance.

    // src/routes/my-page.server.jsx
    
    export async function api(request) {
      if (request.method === 'POST') {
        // do some work here...
      }
    
      return new Request(request.url, {method: 'GET'});
    }
    
    export default function Page() {
      return (
        <form action="/my-page" method="POST">
          ...
        </form>
      );
    }

    In the previous example, a POST request to /my-page would run the API handler and automatically continue with the server component rendering (GET). This is useful for handling HTML forms without waterfall requests.

  • #1047 5268bf85 Thanks @jplhomer! - Restore scroll position when navigating using the back and forward buttons.
  • #1046 3947d53a Thanks @michenly! - Fixed server Cookie bug where initializing with empty string will resulted in 1 item in the Cookies Map.

0.14.0

Minor Changes

  • #1028 ba174588 Thanks @michenly! - Starting from SF API version 2022-04, the preferred way to request translatable resources is using the @inContext directive. See the API docs on how to do this and which resources have translatable properties.

    This causes a breaking change to the useShopQuery hook. The locale property has been removed from the argument object; Accept-Language is no longer being send with every request, and we are no longer using locale as part of the cache key.

    The useShop hook will now return the languageCode key, which is the first two characters of the existing locale key.

    Both locale & languageCode values are also now capitalized to make it easier to pass into a GraphQL @inContext directive.

  • #1020 e9529bc8 Thanks @jplhomer! - Preload Link URLs by default when a user signals intent to visit the URL. This includes hovering or focusing on the URL. To disable preloading, pass <Link preload={false} /> to the component.

Patch Changes

  • #1017 4c87fb63 Thanks @frandiox! - Do not cache Storefront API responses that contain GraphQL errors (amend previous fix).
  • #1026 836b064d Thanks @frehner! - Updated the Typescript types and GraphQL schema to the newest updates from Storefront API 2022-04. Of note in this update is the ability to skip edges and go directly to node, for example: product.nodes[0] instead of product.edges[0].node
  • #1032 03488083 Thanks @jplhomer! - Catch hydration errors related to experimental server components bugs and prevent them from being logged in production.

0.13.2

Patch Changes

0.13.1

Patch Changes

  • #1003 d8a9c929 Thanks @jplhomer! - Update useShopQuery to accept a custom Storefront API secret token, and forward the Buyer IP.

0.13.0

Minor Changes

  • #922 b5eaddc1 Thanks @frehner! - Fragments and their related types have been removed:

    • ExternalVideoFragment and ExternalVideoFragmentFragment
    • Model3DFragment and Model3DFragmentFragment
    • ImageFragment and ImageFragmentFragment
    • MoneyFragment and MoneyFragmentFragment
    • UnitPriceFragment and UnitPriceFragmentFragment
    • VideoFragment and VideoFragmentFragment
    • MetafieldFragment and MetafieldFragmentFragment
    • Seo fragments and types:
      • DefaultPageSeoFragment and DefaultPageSeoFragmentFragment
      • HomeSeoFragment and HomeSeoFragmentFragment
      • ProductSeoFragment and ProductSeoFragmentFragment
      • CollectionSeoFragment and CollectionSeoFragmentFragment
      • PageSeoFragment and PageSeoFragmentFragment
    • MediaFile fragments and types:
      • MediaFileFragment and MediaFileFragmentFragment
      • MediaFileFragment_ExternalVideo_Fragment
      • MediaFileFragment_MediaImage_Fragment
      • MediaFileFragment_Model3d_Fragment
      • MediaFileFragment_Video_Fragment
    • ProductFragment and ProductFragmentFragment

    These fragments have been removed to reduce the chances of over-fetching (in other words, querying for fields you don't use) in your GraphQL queries. Please refer to the Storefront API documentation for information and guides.

  • #912 de0e0d6a Thanks @blittle! - Change the country selector to lazy load available countries. The motivation to do so is that a lot of countries come with the Demo Store template. The problem is 1) the graphql query to fetch them all is relatively slow and 2) all of them get serialized to the browser in each RSC response.

    This change removes availableCountries from the LocalizationProvider. As a result, the useAvailableCountries hook is also gone. Instead, the available countries are loaded on demand from an API route.

    Migratation steps:

    Create an API route to retrieve available countries:

    export async function api(request, {queryShop}) {
      const {
        data: {
          localization: {availableCountries},
        },
      } = await queryShop({
        query: QUERY,
      });
    
      return availableCountries.sort((a, b) => a.name.localeCompare(b.name));
    }
    
    const QUERY = `
      query Localization {
        localization {
          availableCountries {
            isoCode
            name
            currency {
              isoCode
            }
          }
        }
      }
    `;

    Then within your client code, query the API route with a useEffect hook:

    const [countries, setCountries] = useState([]);
    
    useEffect(() => {
      fetch('/api/countries')
        .then((resp) => resp.json())
        .then((c) => setCountries(c))
        .catch((e) => setError(e))
        .finally(() => setLoading(false));
    }, []);

    See an example on how this could be done inside the Demo Store template country selector

  • #698 6f30b9a1 Thanks @jplhomer! - Basic end-to-end tests have been added to the default Hydrogen template. You can run tests in development:

    yarn test

    Or in continuous-integration (CI) environments:

    yarn test:ci
  • #932 507c5cbf Thanks @jplhomer! - Adds CSS Modules support. Hydrogen now includes a Vite plugin that collects styles for each CSS Module and exports them to a StyleTag component. To use CSS Modules in your Hydrogen app, you must render the style tag in the component along with your styles:

    import * as styles from './styles.module.css';
    
    export default MyComponent() {
      return (
        <div className={styles.wrapper}>
          // A style is rendered inline
          <styles.StyleTag />
          <p>Hello</p>
        </div>
      );
    }

    Explore an example implementation of CSS Modules in GitHub.

  • #846 58c823b5 Thanks @blittle! - ## New <Route> Component

    The <Route> component is available for routes not defined by the file system. The <Route> component must be used within the <Router> component.

    // app.server.jsx
    
    function App({routes, ...serverProps}) {
      return (
        <Suspense fallback={<LoadingFallback />}>
          <ShopifyProvider shopifyConfig={shopifyConfig}>
            <CartProvider>
              <DefaultSeo />
              <Router serverProps={serverProps}>
                <Route path="/custom" page={<CustomRoute />} />
              </Router>
            </CartProvider>
          </ShopifyProvider>
        </Suspense>
      );
    }
    
    function CustomRoute() {
      return <h1>Custom route</h1>;
    }

    <Route> accepts two props:

    Property Type Required Description
    path string Yes The URL path where the route exists. The path can contain variables. For example, /products/:handle.
    page A rendered Server Component reference Yes A reference to a React Server Component that's rendered when the route is active.

    Changes to <Router>

    You can have multiple <Route> and <FileRoutes> components in your app. Hydrogen will only render one route for each request — whichever it finds first. This means the <Router> component no longer takes fallback as a prop. It also doesn't need serverProps. Instead, to render a 404 "Not Found" page, add <Route path="*" page={<NotFound />} /> to your app. Make sure it's the last <Route> defined inside your app:

    function App({routes, ...serverProps}) {
      return (
        <ShopifyProvider shopifyConfig={shopifyConfig}>
          <CartProvider>
            <DefaultSeo />
    -       <Router
    -         fallback={<NotFound response={serverProps.response} />}
    -         serverProps={serverProps}
    -       >
    +       <Router>
              <FileRoutes routes={routes} />
    +         <Route path="*" page={<NotFound />} />
            </Router>
          </CartProvider>
        </ShopifyProvider>
      );
    }

    Changes to <FileRoutes>

    The <FileRoutes> component now accepts two additional optional props:

    Property Type Required Default Value Description
    basePath string No "/" A path that's prepended to all file routes.
    dirPrefix string No "./routes" The portion of the file route path that shouldn't be a part of the URL.

    You need to modify dirPrefix if you want to import routes from a location other than src/routes.

    You can modify basePath if you want to prefix all file routes. For example, you can prefix all file routes with a locale:

    <Router>
      <FileRoutes basePath={`/${locale}`} routes={routes} />
      <Route path="*" page={<NotFound />} />
    </Router>

    New useRouteParams() hook

    You can use the useRouteParams() hook to retrieve the parameters of an active route. The hook is available in both server and client components:

    // products/[handle].server.jsx
    
    import {useRouteParams} from '@shopify/hydrogen';
    
    export default function Product() {
      const {handle} = useRouteParams();
      // ...
    }
    // ProductDetails.client.jsx
    import {useRouteParams} from '@shopify/hydrogen/client';
    
    export default function ProductDetails() {
      const {handle} = useRouteParams();
      // ...
    }
  • #842 626e58ee Thanks @wizardlyhel! - Removed the Rawhtml component.

    Upgrade your project by replacing references to the RawHtml component to follow React's dangerouslySetInnerHTML:

    Change all RawHtml component

    <RawHtml string="<p>Hello world</p>" />

    to jsx equivalent

    <div dangerouslySetInnerHTML={{__html: '<p>Hello world</p>'}} />

Patch Changes

  • #870 4c0fcd8f Thanks @frandiox! - Remove useQuery hook from client exports to avoid leaking server logic to the browser.
  • #965 cdad13ed Thanks @blittle! - Fix server redirects to work properly with RSC responses. For example, the redirect component within the Demo Store template needs to change:

    export default function Redirect({response}) {
    -  response.redirect('/products/snowboard');
    -  return <div>This page is redirected</div>;
    +  return response.redirect('/products/snowboard');
    }

    This server component is rendered two ways:

    1. When an app directly loads the redirect route, the server will render a 300 redirect with the proper location header.
    2. The app is already loaded, but the user navigates to the redirected route. We cannot 300 respond in this scenario, instead response.redirect(...) returns a component which will redirect on the client.
  • #758 0bee3af0 Thanks @frandiox! - Upgrade to React experimental version 0.0.0-experimental-2bf7c02f0-20220314.

    To upgrade your Hydrogen app, change the pinned version of react and react-dom in your package.json file to this version, or run:

    yarn add @shopify/hydrogen [email protected] [email protected]
  • #878 587aa3e6 Thanks @frandiox! - Fix preloading queries in workers to prevent waterfall requests.

    Breaking change: fetchBuilder no longer accepts a Request argument. Instead, it now accepts a url: string and options: FetchInit:

    -fetchBuilder(new Request('https://my.endpoint.com', {headers: {foo: 'bar'}}));
    +fetchBuilder('https://my.endpoint.com', {headers: {foo: 'bar}});
  • #923 993be985 Thanks @frandiox! - Provide a Logger option to use GraphQL and disable by default. Improve logging of unused query properties.

0.12.0

Minor Changes

  • 8271be8 Thanks @michenly! - Export Seo components Fragement and use them in the Demo Store template.
  • #827 745e8c0 Thanks @michenly! - Move any static Fragment properties on components to the entry point @shopify/hydrogen/fragments. The migration diff are as follows:

    - import {ExternalVideoFragment} from '@shopify/hydrogen';
    + import {ExternalVideoFragment} from '@shopify/hydrogen/fragments';
    - import type {ExternalVideoFragmentFragment} from '@shopify/hydrogen';
    + import type {ExternalVideoFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {ImageFragment} from '@shopify/hydrogen';
    + import {ImageFragment} from '@shopify/hydrogen/fragments';
    - import type {ImageFragmentFragment} from '@shopify/hydrogen';
    + import type {ImageFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {MediaFileFragment} from '@shopify/hydrogen';
    + import {MediaFileFragment} from '@shopify/hydrogen/fragments';
    - import type {MediaFileFragmentFragment} from '@shopify/hydrogen';
    + import type {MediaFileFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {MetafieldFragment} from '@shopify/hydrogen';
    + import {MetafieldFragment} from '@shopify/hydrogen/fragments';
    - import type {MetafieldFragmentFragment} from '@shopify/hydrogen';
    + import type {MetafieldFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {Model3DFragment} from '@shopify/hydrogen';
    + import {Model3DFragment} from '@shopify/hydrogen/fragments';
    - import type {Model3DFragmentFragment} from '@shopify/hydrogen';
    + import type {Model3DFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {MoneyFragment} from '@shopify/hydrogen';
    + import {MoneyFragment} from '@shopify/hydrogen/fragments';
    - import type {MoneyFragmentFragment} from '@shopify/hydrogen';
    + import type {MoneyFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {ProductProviderFragment} from '@shopify/hydrogen';
    + import {ProductProviderFragment} from '@shopify/hydrogen/fragments';
    - import type {ProductProviderFragmentFragment} from '@shopify/hydrogen';
    + import type {ProductProviderFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {UnitPriceFragment} from '@shopify/hydrogen';
    + import {UnitPriceFragment} from '@shopify/hydrogen/fragments';
    - import type {UnitPriceFragmentFragment} from '@shopify/hydrogen';
    + import type {UnitPriceFragmentFragment} from '@shopify/hydrogen/fragments';
    - import {VideoFragment} from '@shopify/hydrogen';
    + import {VideoFragment} from '@shopify/hydrogen/fragments';
    - import type {VideoFragmentFragment} from '@shopify/hydrogen';
    + import type {VideoFragmentFragment} from '@shopify/hydrogen/fragments';
  • #809 47f23f9 Thanks @frehner! - Upgrade default Storefront API to version '2022-04'. Some components have been updated to use the 2022-04 features and types as well.

    One important change is that the 2022-04 Storefront API no longer encodes object IDs: see more details here. Because of this, Hydrogen will no longer decode IDs, either, which will cause issues if you are using a previous version of the Storefront API with Hydrogen components.

  • #780 122a5c5 Thanks @jplhomer! - Adds queryShop helper to API routes. This makes it easy to query the Storefront API, similar to how useShopQuery is available in server components:

    // my-api.server.js
    
    export default function api(request, {queryShop}) {
      return await queryShop({
        query: `query ShopName { shop { name } }`,
      });
    }

    queryShop accepts a single argument object with the following properties:

    Property Type Required
    query string | ASTNode Yes
    variables Record<string, any> No
    locale string. Defaults to the locale value from the LocalizationProvider component. No

    Important: In order to use queryShop, you should pass shopifyConfig to renderHydrogen inside App.server.jsx:

    -export default renderHydrogen(App, {routes});
    +export default renderHydrogen(App, {shopifyConfig, routes});
  • #712 6368968 Thanks @blittle! - Routing in Hydrogen has been updated according to Custom Routes proposal. Specifically, a new Router component has been added, and DefaultRoutes has been renamed to FileRoutes, along with other minor changes. Custom route components are not implemented yet.

    Follow these steps to upgrade your App.server.jsx file:

    1. Rename the parameter pages to routes when calling renderHydrogen.
    2. Rename the DefaultRoutes component to FileRoutes.
    3. Add the new Router component as a parent of FileRoutes and pass fallback and serverProps props (previously in DefaultRoutes).
    4. Rename src/pages directory to src/routes and update the glob import in App.server.jsx to import.meta.globEager('./routes/**/*.server.[jt](s|sx)').

    Full example of App.server.jsx

    import renderHydrogen from '@shopify/hydrogen/entry-server';
    import {Router, FileRoutes, ShopifyProvider} from '@shopify/hydrogen';
    import {Suspense} from 'react';
    import shopifyConfig from '../shopify.config';
    import DefaultSeo from './components/DefaultSeo.server';
    import NotFound from './components/NotFound.server';
    import LoadingFallback from './components/LoadingFallback';
    import CartProvider from './components/CartProvider.client';
    
    function App({routes, ...serverProps}) {
      return (
        <Suspense fallback={<LoadingFallback />}>
          <ShopifyProvider shopifyConfig={shopifyConfig}>
            <CartProvider>
              <DefaultSeo />
              <Router fallback={<NotFound />} serverProps={serverProps}>
                <FileRoutes routes={routes} />
              </Router>
            </CartProvider>
          </ShopifyProvider>
        </Suspense>
      );
    }
    
    const routes = import.meta.globEager('./routes/**/*.server.[jt](s|sx)');
    export default renderHydrogen(App, {shopifyConfig, routes});

Patch Changes

  • #850 74b14e4 Thanks @blittle! - Ignore when boomerang doesn't load. This often happens when a adblocker is present on the client. There is no longer an uncaught promise exception in the console.
  • #803 7528bf4 Thanks @frandiox! - Avoid accessing undefined global __flight as a side effect of another unknown error.
  • #825 1215fdb Thanks @michenly! - @shopify/hydrogen will no longer export the following types

    • MediaFileProps
    • VideoProps
    • ImageProps
    • ExternalVideoProps
    • RawHtmlProps
    • AddToCartButtonProps
    • ModelViewerProps
    • MoneyProps
    • BuyNowButtonProps
    • BuyNowButtonPropsWeControl
    • ShopPayButtonProps

    Any Component props type should be typed instead with React.ComponentProps<typeof MyComponent>.

  • #792 8aad0b5 Thanks @frandiox! - Attributes from <html> and <body> elements in index.html are now included in the SSR response.
  • #786 d1ecaf7 Thanks @frehner! - Updated graphql-codegen, which updates the Typescript types available for each Storefront API object
  • #849 e64fa17 Thanks @blittle! - Fix server the server to only log once for the full time it takes to stream render a page
  • #813 b1b959c Thanks @frandiox! - Remove Router client-only logic from server bundle and avoid extra waterfall requests during Hydration. Extract part of the client bundle into separate modules that can be loaded in parallel.

0.11.1

Patch Changes

  • #761 1142647 Thanks @frehner! - Fix issue with components that take in the as prop not validating other props when a component is passed to as.
  • #774 052f148 Thanks @frandiox! - Fix internal url usage in platforms like Vercel, which already provides protocol and host in request.url.
  • #775 d5b7ee1 Thanks @cartogram! - In cases where the initialVariantId is missing on the <ProductProvider />, the selectedVariantId in the returned object from useProduct() will now use the first available variant or the first variant (if non are available).
  • #773 b6a053e Thanks @frandiox! - Fix server bundle name in cases where CSS or images are imported in server components.
  • #763 ea2857a Thanks @frehner! - Client-side apps now have React's StrictMode component wrapping the whole app, with an option to disable it. If you do turn it off, it is recommended that you still include the StrictMode component at as high of a level as possible in your React tree.

    See also React 17's docs on StrictMode, and React 18's updates to StrictMode.

0.11.0 - 2022-02-24

Added

  • New React hook useScriptLoader is available to more easily load external scripts
  • Add totalQuantity to the returned object from useCart()
  • Export ProductPrice and ProductMetafield standalone components
  • Added useUrl hook that allows the consumer to get the current url in server or client component
  • Added logging option showCacheApiStatus and cacheControlHeader by @wizardlyhel in #472
  • Pass HYDROGEN_ASSET_BASE_URL into config to set base URL for compiled assets
  • Introduce Hydrogen the <Link> component and useNavigate hook for routing
  • Add a default virtual entry-client in /@shopify/hydrogen/entry-client that can be used in index.html
  • Enable early hydration when streaming
  • Add variantId prop to <ProductMetafield /> component #730
  • Add query timing logging option showQueryTiming #699
  • Add variantId prop to <ProductPrice /> component
  • Add preload option to useQuery and useShopQuery #700

Breaking Change

  • <Model3D> has been renamed to <ModelViewer>
  • <Product /> and <CartLine /> aliases have been removed; use the original components <ProductProvider /> and <CartLineProvider /> instead. Their nested component aliases, such as <Product.Image />, have also been removed; in this example you should use <ProductImage />.
  • Merge /src/entry-server.jsx entry point into App.server.jsx
  • The following components had their prop name renamed. Refer to the documentation or #627 for more details.
    • <ExternalVideo />: renamed video prop to data
    • <Video />: renamed video prop to data
    • <Image>: renamed image prop to data
    • <MediaFile>: renamed media prop to data
    • <ModelViewer>: renamed model prop to data
    • <Metafield>: renamed metafield prop to data
    • <Money>: renamed money prop to data
    • <UnitPrice>: renamed unitPrice prop to data, unitPriceMeasurement prop to measurement
    • <ProductProvider>: renamed product prop to data
    • <CartProvider>: renamed cart prop to data
  • Helmet component has been renamed to Head
  • Remove the <SelectedVariantBuyNowButton /> component in favour of using <BuyNowButton variantId={product.selectedVariant.id} />
  • <SelectedVariantAddToCartButton /> has been removed; the <AddToCartButton /> will now use the selectedVariant by default.
  • Remove the <SelectedVariantImage /> component in favour of using <Image data={product.selectedVariant.image} />
  • Remove the <SelectedVariantMetafield /> component in favour of using <ProductMetafield variantId={product.selectedVariant.id} />
  • Remove the <SelectedVariantShopPayButton /> component in favour of using <ShopPayButton variantId={product.selectedVariant.id} />
  • Remove the <SelectedVariantPrice/> and <SelectedVariantUnitPrice/> component in favour of using <ProductPrice variantId={product.selectedVariant.id} />

Changed

  • Change /react RSC path to /__rsc
  • <ShopifyProvider> can again be used in server components
  • Use hashes as client component ids instead of absolute paths
  • Transition away from deprecated currency selector in favor of country selector
  • Simplify Helmet usage and make it compatible with RSC
  • The Seo.client component has been moved from src/components to @shopify/hydrogen. The props of the Seo.client component also changed to always take in type and data. Refer to the Seo component reference for more details. #539
  • Standardize cache control header into caching strategies by @wizardlyhel in #629
  • Target future release to use '2022-01' API Version
  • Correct Typescript issue where as was a default prop for all components when it should not be
  • Update types and docs for useCart() hook and <CartProvider>
  • Track page load performance
  • The following money components no longer allow the function-as-a-child (also known as "render props") pattern; see #589
    • <Money> Use useMoney() for customization
    • <CartLinePrice> Use useMoney() for customization
    • <ProductPrice> Use useMoney() for customization
    • <SelectedVariantPrice> Use useMoney() for customization
    • <Metafield> Use useParsedMetafields() for customization
    • <ProductMetafield> Use useParsedMetafields() for customization
    • <SelectedVariantMetafield> Use useParsedMetafields() for customization
    • <UnitPrice> Use useMoney() for customization
    • <CartLines> Use useCart() for customization
  • <Metafield> now renders ratings as a <span> with text instead of stars; multi_line_text_field inside of a <span> instead of a <div>
  • Use featureImage instead of images(first:1) on product query
  • Update react-helmet-async to 1.2.3 and remove our custom types

Fixed

  • Fix index routes. See #562
  • Fix missing server state on SSR pass
  • Fix mobile navigation in example that scrolls the body underneath when shown by @Francismori7 in #582
  • Add charset to content type in HTML responses
  • Fix header shift when cart is opened by @Francismori7 in #600
  • Fix bug where search param is not being pass along during RSC streaming call #623
  • Allow custom entry-client filenames
  • Clear browser fetch cache by @wizardlyhel in #591
  • Cannot redefine property error when updating client components
  • ShopPayButton supports quantities greater than 1. Also fixed issues with IDs in Storefront API version 2022-01
  • Render error in Gallery.client.jsx component when product resource has an external video or no images.
  • Ensure youtube external videos are embed compatible urls
  • Prevent client components from being cached during development
  • Backticks in HTML break RSC hydration.

Removed

  • and components. These components used the “function-as-a-child” pattern which doesn’t allow the children prop to be serialized, preventing them from being rendered within Server components.

Migration

The functionality provided by these components can be replicated using the useCartLine() hook instead.

Example

// Before
function SomeComponent() {
  return (
    <>
      <CartLineSelectedOptions as="ul" className="text-xs space-y-1">
        {({name, value}) => (
          <>
            {name}: {value}
          </>
        )}
      </CartLineSelectedOptions>
      <CartLineAttributes as="ul" className="text-sm space-y-1">
        {({key, value}) => (
          <>
            {key}: {value}
          </>
        )}
      </CartLineAttributes>
    </>
  );
}

// After
function SomeComponent() {
  const {merchandise} = useCartLine();

  return (
    <>
      <ul className="text-xs space-y-1">
        {merchandise.selectedOptions.map(({name, value}) => (
          <li key={name}>
            {name}: {value}
          </li>
        ))}
      </ul>
    </>
  );
}
  • Remove fetch workaround
  • Remove the following hooks. (All the same functionality can be retrieved through the useCart() hook)
    • useCartAttributesUpdateCallback
    • useCartBuyerIdentityUpdateCallback
    • useCartCheckoutUrl
    • useCartCreateCallback
    • useCartDiscountCodesUpdateCallback
    • useCartLinesAddCallback
    • useCartLinesRemoveCallback
    • useCartLinesTotalQuantity
    • useCartLinesUpdateCallback
    • useCartNoteUpdateCallback
  • Remove React Router on the client
  • Remove handleEvent in favor of handleRequest
  • Remove assetHandler parameter in the new handleRequest
  • <SelectedVariantAddToCartButton /> has been removed; the <AddToCartButton /> will now use the selectedVariant by default.
  • Remove the <SelectedVariantImage /> component in favour of using <Image data={product.selectedVariant.image} />
  • Remove the <SelectedVariantMetafield /> component in favour of using <ProductMetafield variantId={product.selectedVariant.id} />
  • Remove the <SelectedVariantBuyNowButton /> component in favour of using <BuyNowButton variantId={product.selectedVariant.id} />
  • Remove the <SelectedVariantShopPayButton /> component in favour of using <ShopPayButton variantId={product.selectedVariant.id} />

0.10.1 - 2022-01-26

Fixed

  • Hot reload for newly added page files

0.10.0 - 2022-01-25

Changed

  • Warn instead of error when a page server component is missing valid exports
  • Adopt upstream version of React Server Components. See #498 for breaking changes
  • Bump to latest version of React experimental to include upstream context bugfix
  • Improve API routes by allowing strings and JS objects to be returned.

Breaking Change

  • The 'locale' option in shopify.config.js had been renamed to 'defaultLocale'
  • Rename graphqlApiVersion to storefrontApiVersion in shopify.config.js

Fixed

0.9.1 - 2022-01-20

Changed

  • Transitive dependency bump.

0.9.0 - 2022-01-20

Added

  • API routes 🎉

Changed

  • Move to undici instead of node-fetch

0.8.3 - 2022-01-13

Added

  • Add optional locale param to useShopQuery to be used as Accept-Language in the store Storefront API query
  • Optional purge query cache per build

Fixed

  • Replace log abbreviations with full text.

0.8.2 - 2022-01-07

Changed

  • Warn when requests take longer than 3000ms instead of erroring
  • useQuery returns an error if the query's fetch was unsuccessful
  • useShopQuery will give error hints to look at shopify.config.js when the Storefront API responds with a 403

Fixed

  • Load logger only once.
  • Do not attempt to decode product IDs, as they are no longer base64-encoded in unstable

0.8.1 - 2022-01-04

Added

  • Detect bot user agents and give bots a non-streamed response.
  • Add global Oxygen.env for server-only environment variables.
  • Logging abstraction with default timing information

Changed

  • Upgrade to latest React 18 experimental version

Fixed

  • Cart decrease button removes at zero quantity

0.8.0 - 2021-12-07

Fixed

  • Export CartLineSelectedOptions properly
  • Fix suspense utility function

0.7.1 - 2021-12-02

Changed

  • Allow useShopQuery to be skippable if no query is passed
  • Remove usage of react-query (Not a breaking change)

Fixed

  • Avoid repeating the same identifier for default and named exports
  • Remove sourcemap warnings

0.7.0 - 2021-11-22

Added

  • Add file reference metafield support
  • Allow custom Model3D poster
  • Support synchronous server redirects

Fixed

  • Binding of waitUntil in playground/server-components-worker
  • Default to retry: false in useQuery
  • Warn and ignore reserved properties in server state
  • Run graphiql middleware before vite, fixing graphiql

0.6.4 - 2021-11-11

Fixed

  • Let Vite handle public assets in development
  • New lines in hydration request break JSON.parse
  • Normalize POSIX separators to support windows #201
  • Scroll to top on app first load
  • Update variantID to variantId #78

0.6.3 - 2021-11-10

Fixed

  • Add trailing slash to user components glob

0.6.2 - 2021-11-10

Fixed

  • Remove CartProvider from BuyNowButton
  • Reading property of null for component props
  • Transform deeply-imported client components
  • Duplicated files and contexts in browser

0.6.1 - 2021-11-08

Changed

  • Transitive dependency bump.

Fixed

  • Do not set headers after they are sent to client

0.6.0 - 2021-11-05

Changed

  • Disable the quantity adjust button when the cart is not idle
  • Use country server state in cart for the inContext directive
  • Use Image url field instead of deprecated originalSrc field
  • Switch to unstable API

Fixed

  • Update interaction prompt and interaction promp style attributes for Model3d
  • Make sure all errors show an error dialog when hydrogen is in dev mode
  • MediaFile component warning on non-Model3D types
  • Remove console logs for caching
  • Lowercased SVG tags in RSC
  • Make the URL search property available via hooks

0.5.8 - 2021-11-04

Fixed

  • Ensure delayed callback is fired for cache purposes in Workers runtimes.

0.5.3 - 2021-11-02

Changed

  • No updates. Transitive dependency bump.

0.5.2 - 2021-11-02

Changed

  • No updates. Transitive dependency bump.

0.5.1 - 2021-11-02

Changed

  • No updates. Transitive dependency bump.

0.5.0 - 2021-11-01

Fixed

  • Update the ServerStateProvider context
  • Add tabIndex to ShopPayButton
  • Update LocalizationProvider query, context, and exports

0.4.3 - 2021-10-29

Added

  • Introduct full-page and sub-request caching API.

0.4.2 - 2021-10-29

Changed

  • Update Model3D props and add binding to model-viewer events

Fixed

  • Add passthoughProps.disabled to AddToCartButton
  • Do not show undefined currency symbol in production

0.4.0 - 2021-10-27

Added

  • Add external image support to Image component

Changed

  • Make CartProvider a client-only concern. #631
  • Use Accept: application/hydrogen as a header when making fetch calls against a Hydrogen page. Useful for Custom Responses.

Fixed

  • Lock model-viewer.js version to 1.8
  • Use the Intl.NumberFormat parts for determining the amount value returned by the useMoney hook
  • Optimize React related dependencies at server start to avoid page reloads
  • Do not throw when storeDomain contains protocol.

0.3.0 - 2021-10-20

Added

  • Export utilities in client bundle

Fixed

  • parseCookies will split only on first =
  • Make BuyNowButton a client component since it uses useEffect
  • Preserve original aspect ratio for product images
  • Invoke CartProvider callbacks before performing the GraphQL mutations
  • Fix the accessible label in the AddToCartButton component when an item is added to cart
  • Cart fetch to return stringified error

Removed

  • Remove sourcemap warnings

0.2.1 - 2021-10-12

Fixed

  • Demo Store template GalleryPreview unique key warning
  • Mitigation for upcoming breaking minor Vite update

0.2.0 - 2021-10-08

Added

  • Added support for images and collections in the ProductProvider component
  • Added more GraphQL fragments for building block components (Metafield, UnitPrice) and updated exports of these fragments

Breaking Change

Fixed

  • Handle products with selling plans

0.1.2 - 2021-09-30

Fixed

  • SSR issue when running Vite 2.6
  • Occasional ProductProviderFragment error when booting Hydrogen dev server #571

0.1.1 - 2021-09-24

Added

  • New GraphQL fragments for Variants, SellingPlans, and SellingPlanGroups

Changed

  • Updated types for the useProductOptions hook

Fixed

  • Dynamic require of "stream" is not supported error in browser logs

0.1.0 - 2021-09-23

Changed

  • No updates. Transitive dependency bump.

1.0.0-alpha.22 - 2021-09-22

Changed

  • No updates. Transitive dependency bump.