Skip to content

Releases: Shopify/hydrogen-v1

@shopify/[email protected]

31 May 21:33
ea002f2
Compare
Choose a tag to compare

Patch Changes

@shopify/[email protected]

31 May 19:07
fe78881
Compare
Choose a tag to compare

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.
  • #1348 211093e5 Thanks @developit! - Fix HTML double-decoding in flight response

  • #1345 331ff3c0 Thanks @frandiox! - Reduce the amount of user app files downloaded in the browser.

  • #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.

  • #1355 c45a45e8 Thanks @jplhomer! - Ensure all Hydrogen components are exported properly

  • #1339 fef4cb84 Thanks @jplhomer! - Use import.meta.env.DEV instead of process.env.LOCAL_DEV to hash asset filenames and show performance metrics debugging

  • #1320 7e9df897 Thanks @jplhomer! - Properly log errors during flight responses

  • #1363 0941d3be Thanks @frandiox! - Remove some server utilities from client build.

[email protected]

24 May 19:44
7cee7e0
Compare
Choose a tag to compare

@shopify/[email protected]

24 May 19:44
7cee7e0
Compare
Choose a tag to compare

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
  • #1216 771786a6 Thanks @wizardlyhel! - Fixes an issue where cached sub-requests were not revalidating properly.

  • #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.

  • #1293 e378ed61 Thanks @jplhomer! - Reverts #1272 and properly escapes terminating script sequences

  • #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

[email protected]

18 May 04:21
bf78563
Compare
Choose a tag to compare

@shopify/[email protected]

18 May 04:21
bf78563
Compare
Choose a tag to compare

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
  • #1290 437b1616 Thanks @jplhomer! - Allow cart queries to be customized by adding a new cartFragment prop to CartProvider. Learn more.

Patch Changes

[email protected]

14 May 01:36
45aae86
Compare
Choose a tag to compare

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();
        ...
      }

@shopify/[email protected]

14 May 01:36
45aae86
Compare
Choose a tag to compare

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

[email protected]

10 May 21:45
e1a4543
Compare
Choose a tag to compare

Patch Changes

@shopify/[email protected]

10 May 21:45
e1a4543
Compare
Choose a tag to compare

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

  • #1214 58ef6d69 Thanks @frehner! - Upgraded SFAPI version to 2022-07

  • #1232 d3956d62 Thanks @arlyxiao! - Upgrade body-parser in hydrogen package

Patch Changes

  • #1211 f3d26511 Thanks @wizardlyhel! - Build chunks are inside assets folder

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

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

  • #1242 c277c688 Thanks @blittle! - Prevent JSON parsing from prototype poisoning vulnerabilities

  • #1210 a844d26e Thanks @blittle! - Add eslint back and fix stale product options