Releases: Shopify/hydrogen-v1
@shopify/[email protected]
@shopify/[email protected]
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 passingnoindex
to theSeo
component:<Seo type="noindex" data={{title: 'Login'}} />
-
#1313
ed1933e3
Thanks @frandiox! - Breaking change: Theroutes
property inhydrogen.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 andfiles
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 newgql
utility is exported from@shopify/hydrogen
that replacesgraphql-tag
dependency when usinguseShopQuery
. It helps reducing bundle size in production when compared to the originalgraphql-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: Theresponse.send()
function has been removed. Useexport 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
andalt
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! - Useimport.meta.env.DEV
instead ofprocess.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]
@shopify/[email protected]
Minor Changes
-
#1327
ce56311f
Thanks @frehner! - Breaking Change:<Money />
updates and<UnitPrice />
Removed.<UnitPrice/>
has been removed<Money/>
has two new props:measurement
andmeasurementSeparator
which do the work thatUnitPrice
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 ChangeThe
<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 theenableStreaming
option insidehydrogen.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]
@shopify/[email protected]
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; useshopifyImageLoader
instead, which is available to run both server- and client-side.- The TypeScript experience with
<Image/>
is improved; props will be validated better, andloader
andloaderOptions
will be better typed - When using the
src
prop,width
andheight
are now required - When using the
data
prop,data.width
anddata.height
orwidth
andheight
props are required - The
src
anddata
props are mutually exclusive - The
loader
prop now receives a singular param as an object options
has been merged withloaderOptions
. When using thedata
prop,loaderOptions
will be the options for Shopify CDN images. When using thesrc
prop,loaderOptions
will be whatever you define them to be.- The TypeScript type
ImageSizeOptions
is now namedShopifyLoaderOptions
- The TypeScript type
ImageLoaderOptions
is now namedShopifyLoaderParams
- The
priority
prop was removed; use the HTML-standardloading
prop instead
<Video/>
- The
<Video/>
component'soptions
props was renamed toimagePreviewOptions
to add clarity as to what the options were for. imagePreviewOptions
matches the (newly updated) shape of<Image/>
'sloaderOptions
-
#1290
437b1616
Thanks @jplhomer! - Allow cart queries to be customized by adding a newcartFragment
prop toCartProvider
. Learn more.
Patch Changes
[email protected]
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:useCountry()
hook now only returns the currently selected country. ThesetCountry()
method has been removed.- The
useCountry()
hook expects acountryCode
andcountryName
to be a part of the user session. - The example
/countries
API route has been updated to accept aPOST
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 ( ... ); }
- Each server component page that depends on the selected country pulls it from the session with
useSession()
, rather thanserverProps
.
// 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]
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:useCountry()
hook now only returns the currently selected country. ThesetCountry()
method has been removed.- The
useCountry()
hook expects acountryCode
andcountryName
to be a part of the user session. - The example
/countries
API route has been updated to accept aPOST
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 ( ... ); }
- Each server component page that depends on the selected country pulls it from the session with
useSession()
, rather thanserverProps
.
// 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]
@shopify/[email protected]
Minor Changes
-
#1065
81ae47fd
Thanks @frandiox! - A new config filehydrogen.config.js
replaces the existingshopify.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
(orhydrogen.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 fromrenderHydrogen()
: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 toshopifyConfig
: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. -
#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
andwithoutTrailingZerosAndCurrency
<Money />
now has two additional and optional props:withoutTrailingZeros
andwithoutCurrency
. -
#1242
c277c688
Thanks @blittle! - Prevent JSON parsing from prototype poisoning vulnerabilities -
#1210
a844d26e
Thanks @blittle! - Add eslint back and fix stale product options