Skip to content

v0.10.0

Compare
Choose a tag to compare
@blittle blittle released this 25 Jan 19:16
· 1465 commits to main since this release
8491fb5

Hydrogen v0.10 includes a new server components strategy, a new local preview command, and several important breaking changes. Please read the changes carefully to learn how to upgrade. Thank you for helping us improve Hydrogen during the developer preview!

What's Changed

✨ New Hydrogen apps will includes a yarn preview command! This runs a local version of a sandboxed v8 runtime that mimics production runtimes like Oxygen and Cloudflare Workers. To test it in a new project, run yarn build && yarn preview. For existing apps, run yarn build && npx @shopify/hydrogen-cli preview. This requires Node >= v16.

Breaking Changes:

  1. Hydrogen now uses Meta's experimental version of React Server Components. Make sure to upgrade react and react-dom to the latest experimental version: yarn add @shopify/hydrogen [email protected] [email protected]
  2. We've removed react-router on the server. Instead of relying on useParams() to get URL parameters, params are passed directly to page server components:
export default function Product() {
    const { handle } = useParams();
    ...
}

// changes to

export default function Product({ params }) {
    const { handle } = params;
    ...
}
  1. Shopify configuration should no longer be imported and passed inside App.server.jsx, but instead in entry-client.jsx and entry-server.jsx:
// /App.server.jsx
import {DefaultRoutes} from '@shopify/hydrogen';
import {Suspense} from 'react';

import DefaultSeo from './components/DefaultSeo.server';
import NotFound from './components/NotFound.server';
import AppClient from './App.client';
import LoadingFallback from './components/LoadingFallback';

export default function App({log, pages, ...serverState}) {
  return (
    <Suspense fallback={<LoadingFallback />}>
      <AppClient helmetContext={serverState.helmetContext}>
        <DefaultSeo />
        <DefaultRoutes
          pages={pages}
          serverState={serverState}
          log={log}
          fallback={<NotFound />}
        />
      </AppClient>
    </Suspense>
  );
}
// /entry-server.jsx
import renderHydrogen from '@shopify/hydrogen/entry-server';
import shopifyConfig from '../shopify.config';

import App from './App.server';

const pages = import.meta.globEager('./pages/**/*.server.[jt](s|sx)');

export default renderHydrogen(App, {shopifyConfig, pages});
// /entry-client.jsx
import renderHydrogen from '@shopify/hydrogen/entry-client';
import shopifyConfig from '../shopify.config';

function ClientApp({children}) {
  return children;
}

export default renderHydrogen(ClientApp, {shopifyConfig});
  1. There's a new App.client.jsx component:
// /App.client.jsx
import {HelmetProvider} from '@shopify/hydrogen/client';
import CartProvider from './components/CartProvider.client';

/**
 *  Setup client context, though the children are most likely server components
 */
export default function ClientApp({helmetContext, children}) {
  return (
    <HelmetProvider context={helmetContext}>
      <CartProvider>{children}</CartProvider>
    </HelmetProvider>
  );
}
  1. locale is now defaultLocale in shopify.config.js
  2. graqhqlApiVersion is now storefrontApiVersion in shopify.config.js
  3. If you’re using the starter template layout component, remove the useCartUI hook:
// /src/components/Layout.server.jsx
import {
  Image,
  useShopQuery,
  flattenConnection,
  LocalizationProvider,
} from '@shopify/hydrogen';
import gql from 'graphql-tag';

import Header from './Header.client';
import Footer from './Footer.server';
import Cart from './Cart.client';
import {Suspense} from 'react';

/**
 * A server component that defines a structure and organization of a page that can be used in different parts of the Hydrogen app
 */
export default function Layout({children, hero}) {
  const {data} = useShopQuery({
    query: QUERY,
    variables: {
      numCollections: 3,
    },
    cache: {
      maxAge: 60,
      staleWhileRevalidate: 60 * 10,
    },
  });
  const collections = data ? flattenConnection(data.collections) : null;
  const products = data ? flattenConnection(data.products) : null;
  const storeName = data ? data.shop.name : '';

  return (
    <LocalizationProvider>
      <div className="absolute top-0 left-0">
        <a
          href="#mainContent"
          className="p-4 focus:block sr-only focus:not-sr-only"
        >
          Skip to content
        </a>
      </div>
      <div className="min-h-screen max-w-screen text-gray-700 font-sans">
        {/* TODO: Find out why Suspense needs to be here to prevent hydration errors. */}
        <Suspense fallback={null}>
          <Header collections={collections} storeName={storeName} />
          <Cart />
        </Suspense>
        <main role="main" id="mainContent" className="relative bg-gray-50">
          {hero}
          <div className="mx-auto max-w-7xl p-4 md:py-5 md:px-8">
            {children}
          </div>
        </main>
        <Footer collection={collections[0]} product={products[0]} />
      </div>
    </LocalizationProvider>
  );
}

const QUERY = gql`
  query indexContent($numCollections: Int!) {
    shop {
      name
    }
    collections(first: $numCollections) {
      edges {
        node {
          description
          handle
          id
          title
          image {
            ...ImageFragment
          }
        }
      }
    }
    products(first: 1) {
      edges {
        node {
          handle
        }
      }
    }
  }
  ${Image.Fragment}
`;

All changes:

New Contributors

Full Changelog: Shopify/hydrogen@v0.9.1...v0.10.0