@shopify/create-hydrogen 5.0.24 → 5.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/assets/hydrogen/bundle/analyzer.html +155 -148
  2. package/dist/assets/hydrogen/starter/CHANGELOG.md +131 -49
  3. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +1 -1
  4. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +1 -1
  5. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +1 -1
  6. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +62 -29
  7. package/dist/assets/hydrogen/starter/app/components/Header.tsx +1 -1
  8. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
  9. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +2 -2
  10. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +1 -1
  11. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +8 -3
  12. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +3 -11
  13. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +2 -6
  14. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -2
  15. package/dist/assets/hydrogen/starter/app/entry.server.tsx +5 -3
  16. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +7 -4
  17. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -1
  18. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +4 -1
  19. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +10 -5
  20. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +3 -2
  21. package/dist/assets/hydrogen/starter/app/lib/context.ts +34 -17
  22. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +1 -0
  23. package/dist/assets/hydrogen/starter/app/lib/orderFilters.ts +90 -0
  24. package/dist/assets/hydrogen/starter/app/lib/redirect.ts +1 -1
  25. package/dist/assets/hydrogen/starter/app/lib/session.ts +1 -1
  26. package/dist/assets/hydrogen/starter/app/lib/variants.ts +1 -1
  27. package/dist/assets/hydrogen/starter/app/root.tsx +23 -18
  28. package/dist/assets/hydrogen/starter/app/routes/$.tsx +2 -2
  29. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +2 -2
  30. package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +2 -3
  31. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +12 -8
  32. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +4 -3
  33. package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +1 -1
  34. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +15 -11
  35. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +47 -22
  36. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +152 -23
  37. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +11 -8
  38. package/dist/assets/hydrogen/starter/app/routes/account.tsx +16 -4
  39. package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +2 -2
  40. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -3
  41. package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +3 -2
  42. package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +2 -2
  43. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +6 -10
  44. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +10 -7
  45. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +13 -7
  46. package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +3 -2
  47. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +13 -9
  48. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +8 -11
  49. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +6 -6
  50. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +10 -7
  51. package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +3 -2
  52. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +8 -6
  53. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +7 -4
  54. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +19 -13
  55. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +9 -6
  56. package/dist/assets/hydrogen/starter/app/routes/search.tsx +14 -14
  57. package/dist/assets/hydrogen/starter/app/routes/sitemap.$type.$page[.xml].tsx +2 -3
  58. package/dist/assets/hydrogen/starter/app/routes.ts +1 -1
  59. package/dist/assets/hydrogen/starter/app/styles/app.css +53 -1
  60. package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +47 -13
  61. package/dist/assets/hydrogen/starter/env.d.ts +1 -39
  62. package/dist/assets/hydrogen/starter/eslint.config.js +35 -52
  63. package/dist/assets/hydrogen/starter/package.json +14 -15
  64. package/dist/assets/hydrogen/starter/react-router.config.ts +9 -3
  65. package/dist/assets/hydrogen/starter/server.ts +7 -7
  66. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1 -1
  67. package/dist/assets/hydrogen/starter/tsconfig.json +17 -13
  68. package/dist/assets/hydrogen/starter/vite.config.ts +4 -1
  69. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +13 -20
  70. package/dist/assets/hydrogen/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +37 -0
  71. package/dist/chunk-2LZQLWDR.js +1189 -0
  72. package/dist/{chunk-EO6F7WJJ.js → chunk-6YUUFKYO.js} +1 -1
  73. package/dist/chunk-AUULK6IN.js +5 -0
  74. package/dist/chunk-CJKPLQJ7.js +51 -0
  75. package/dist/{chunk-MNT4XW23.js → chunk-LBUW5UHX.js} +1 -1
  76. package/dist/chunk-RUCJI22O.js +3 -0
  77. package/dist/{chunk-PMDMUCNY.js → chunk-VXJIQGAB.js} +1 -1
  78. package/dist/chunk-Y5VZE2FH.js +32 -0
  79. package/dist/chunk-ZLNTSIDN.js +2 -0
  80. package/dist/create-app.js +293 -288
  81. package/dist/{del-72VO4HYK.js → del-VDYQZFAQ.js} +1 -1
  82. package/dist/devtools-3BYEW2L2.js +8 -0
  83. package/dist/error-handler-XRI3ZDLO.js +2 -0
  84. package/dist/is-wsl-52AELLDM.js +2 -0
  85. package/dist/{morph-3JSBLNUD.js → morph-S2LU6PQ4.js} +7 -7
  86. package/dist/{multipart-parser-QIHQVIZA.js → multipart-parser-MX4R5XJM.js} +1 -1
  87. package/dist/open-PMJ32HTM.js +2 -0
  88. package/dist/out-U7AI7XUQ.js +2 -0
  89. package/package.json +4 -2
  90. package/dist/chokidar-FXMI63T6.js +0 -12
  91. package/dist/chunk-3LZ6M5C2.js +0 -3
  92. package/dist/chunk-D7CI46F7.js +0 -10
  93. package/dist/chunk-FB327AH7.js +0 -5
  94. package/dist/chunk-MZPD7BFF.js +0 -23
  95. package/dist/chunk-QUKX7CP5.js +0 -1180
  96. package/dist/chunk-UASQ33JG.js +0 -45
  97. package/dist/chunk-VMIOG46Y.js +0 -2
  98. package/dist/devtools-DGRGSZU7.js +0 -8
  99. package/dist/error-handler-YXM2BB34.js +0 -2
  100. package/dist/is-wsl-LL6KGQIK.js +0 -2
  101. package/dist/open-OD6DRFEG.js +0 -2
  102. package/dist/out-DXB3K325.js +0 -2
@@ -1,5 +1,96 @@
1
1
  # skeleton
2
2
 
3
+ ## 2025.7.0
4
+
5
+ ### Major Changes
6
+
7
+ - Update Storefront API and Customer Account API to version 2025-07 ([#3082](https://github.com/Shopify/hydrogen/pull/3082)) by [@juanpprieto](https://github.com/juanpprieto)
8
+
9
+ This update includes:
10
+ - Updated API version constants to 2025-07
11
+ - Regenerated GraphQL types for both Storefront and Customer Account APIs
12
+ - Updated all hardcoded API version references in documentation and tests
13
+ - Regenerated skeleton template types
14
+ - Updated skeleton's @shopify/cli dependency to ~3.83.3
15
+
16
+ Breaking changes may occur due to API schema changes between versions.
17
+
18
+ ### Patch Changes
19
+
20
+ - Fix defer/streaming in development & preview ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
21
+
22
+ - Upgrade Miniflare from v2 to v4 in mini-oxygen package. ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
23
+ - Internal MiniOxygen API has been refactored to work with Miniflare v4's new architecture.
24
+ - Simplified MiniOxygen class - no longer extends MiniflareCore.
25
+ - Updated global fetch handling to use Miniflare v4's `outboundService` API.
26
+ - Fixed test infrastructure to use project-relative temporary directories.
27
+ - Added support for Oxygen compatibility parameters (`compatibilityDate`, `compatibilityFlags`).
28
+ - Removed dependency on multiple `@miniflare/*` packages in favor of the consolidated `miniflare` package.
29
+
30
+ - Update and pin react-router to 7.9.2 for 2025.7.0 ([#3138](https://github.com/Shopify/hydrogen/pull/3138)) by [@juanpprieto](https://github.com/juanpprieto)
31
+
32
+ - Add TypeScript ESLint rules for promise handling to prevent Cloudflare Workers errors ([#3146](https://github.com/Shopify/hydrogen/pull/3146)) by [@kdaviduik](https://github.com/kdaviduik)
33
+
34
+ Added `@typescript-eslint/no-floating-promises` and `@typescript-eslint/no-misused-promises` rules to help prevent "The script will never generate a response" errors when deploying to Oxygen/Cloudflare Workers. These rules ensure promises are properly handled with await, return, or void operators, as recommended by [Cloudflare's error documentation](https://developers.cloudflare.com/workers/observability/errors/#the-script-will-never-generate-a-response-errors).
35
+
36
+ - Fixed React Context error that occurred during client-side hydration when using Content Security Policy (CSP) with nonces. The error "Cannot read properties of null (reading 'useContext')" was caused by the `NonceProvider` being present during server-side rendering but missing during client hydration. ([#3082](https://github.com/Shopify/hydrogen/pull/3082)) by [@juanpprieto](https://github.com/juanpprieto)
37
+
38
+ #### Changes for Existing Projects
39
+
40
+ If you have customized your `app/entry.client.tsx` file, you may need to wrap your app with the `NonceProvider` during hydration to avoid this error:
41
+
42
+ ```diff
43
+ // app/entry.client.tsx
44
+ import {HydratedRouter} from 'react-router/dom';
45
+ import {startTransition, StrictMode} from 'react';
46
+ import {hydrateRoot} from 'react-dom/client';
47
+ + import {NonceProvider} from '@shopify/hydrogen';
48
+
49
+ if (!window.location.origin.includes('webcache.googleusercontent.com')) {
50
+ startTransition(() => {
51
+ + // Extract nonce from existing script tags
52
+ + const existingNonce = document
53
+ + .querySelector<HTMLScriptElement>('script[nonce]')
54
+ + ?.nonce;
55
+ +
56
+ hydrateRoot(
57
+ document,
58
+ <StrictMode>
59
+ - <HydratedRouter />
60
+ + <NonceProvider value={existingNonce}>
61
+ + <HydratedRouter />
62
+ + </NonceProvider>
63
+ </StrictMode>,
64
+ );
65
+ });
66
+ }
67
+ ```
68
+
69
+ This ensures the React Context tree matches between server and client rendering, preventing hydration mismatches.
70
+
71
+ #### Package Changes
72
+ - **@shopify/hydrogen**: Exported `NonceProvider` from the main package to allow client-side usage and simplified Vite configuration to improve React Context stability during development
73
+ - **skeleton**: Updated the template's `entry.client.tsx` to include the `NonceProvider` wrapper during hydration
74
+
75
+ - Add `fulfillmentStatus` to CAAPI order query and route ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
76
+
77
+ - Add GraphQL @defer directive support to storefront client ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
78
+
79
+ - Unpin react-router and react-router-dom versions in the skeleton template ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
80
+
81
+ - Add `@inContext` language support to Customer Account API mutations ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
82
+
83
+ - Add order filtering support to the skeleton /account/orders route for Customer Account API flow ([#3125](https://github.com/Shopify/hydrogen/pull/3125)) by [@juanpprieto](https://github.com/juanpprieto)
84
+
85
+ - Updated dependencies [[`6d067665562223ce2865f1c14be54b0b50258bd4`](https://github.com/Shopify/hydrogen/commit/6d067665562223ce2865f1c14be54b0b50258bd4), [`d57782a1ae3fa0017836d6010fb6ac5ab5d25965`](https://github.com/Shopify/hydrogen/commit/d57782a1ae3fa0017836d6010fb6ac5ab5d25965), [`48cbd450699a29a5667bee7174f3856430508ecc`](https://github.com/Shopify/hydrogen/commit/48cbd450699a29a5667bee7174f3856430508ecc), [`6d067665562223ce2865f1c14be54b0b50258bd4`](https://github.com/Shopify/hydrogen/commit/6d067665562223ce2865f1c14be54b0b50258bd4), [`0b4f01c9aa0e09332140a6a4e3114949873fb0f9`](https://github.com/Shopify/hydrogen/commit/0b4f01c9aa0e09332140a6a4e3114949873fb0f9), [`0d165ff280692411712176427bcd7e0df43b56fe`](https://github.com/Shopify/hydrogen/commit/0d165ff280692411712176427bcd7e0df43b56fe), [`ae7bedc89c1968b4a035f421b5ee6908f6376b1b`](https://github.com/Shopify/hydrogen/commit/ae7bedc89c1968b4a035f421b5ee6908f6376b1b), [`ae7bedc89c1968b4a035f421b5ee6908f6376b1b`](https://github.com/Shopify/hydrogen/commit/ae7bedc89c1968b4a035f421b5ee6908f6376b1b), [`75623a5bfdd8d6f0eab0d3547860341c20d9076c`](https://github.com/Shopify/hydrogen/commit/75623a5bfdd8d6f0eab0d3547860341c20d9076c), [`6681f92e84d42b5a6aca153fb49e31dcd8af84f6`](https://github.com/Shopify/hydrogen/commit/6681f92e84d42b5a6aca153fb49e31dcd8af84f6), [`4daf37ea291334b23bd543fdad5673ab7c9a6133`](https://github.com/Shopify/hydrogen/commit/4daf37ea291334b23bd543fdad5673ab7c9a6133)]:
86
+ - @shopify/hydrogen@2026.0.0
87
+
88
+ ## 2025.5.2
89
+
90
+ ### Patch Changes
91
+
92
+ - Fixing the skeleton's Vite Config ([#2958](https://github.com/Shopify/hydrogen/pull/2958)) by [@balazsbajorics](https://github.com/balazsbajorics)
93
+
3
94
  ## 2025.5.1
4
95
 
5
96
  ### Patch Changes
@@ -34,7 +125,6 @@
34
125
  ### Patch Changes
35
126
 
36
127
  - Fix an issue with our starter template where duplicate content can exist on URLs that use internationalized handles. For example, if you have a product handle in english of `the-havoc` and translate it to `das-chaos` in German, duplicate content exists at both: ([#2821](https://github.com/Shopify/hydrogen/pull/2821)) by [@blittle](https://github.com/blittle)
37
-
38
128
  1. https://hydrogen.shop/de-de/products/das-chaos
39
129
  2. https://hydrogen.shop/de-de/products/the-havoc
40
130
 
@@ -54,7 +144,6 @@
54
144
  ### Patch Changes
55
145
 
56
146
  - Moved the `Layout` component back into `root.tsx` to avoid issues with styled errors. ([#2829](https://github.com/Shopify/hydrogen/pull/2829)) by [@ruggishop](https://github.com/ruggishop)
57
-
58
147
  1. If you have a separate `app/layout.tsx` file, delete it and move its default exported component into your `root.tsx`. For example:
59
148
 
60
149
  ```ts
@@ -108,7 +197,6 @@
108
197
  - Support for the Remix future flag `v3_routeConfig`. ([#2722](https://github.com/Shopify/hydrogen/pull/2722)) by [@seanparsons](https://github.com/seanparsons)
109
198
 
110
199
  Please refer to the Remix documentation for more details on `v3_routeConfig` future flag: [https://remix.run/docs/en/main/start/future-flags#v3_routeconfig](https://remix.run/docs/en/main/start/future-flags#v3_routeconfig)
111
-
112
200
  1. Update your `vite.config.ts`.
113
201
 
114
202
  ```diff
@@ -166,13 +254,13 @@
166
254
  1. Add a routes.ts file. This is your new Remix route configuration file.
167
255
 
168
256
  ```ts
169
- import { flatRoutes } from "@remix-run/fs-routes";
170
- import { layout, type RouteConfig } from "@remix-run/route-config";
171
- import { hydrogenRoutes } from "@shopify/hydrogen";
257
+ import {flatRoutes} from '@remix-run/fs-routes';
258
+ import {layout, type RouteConfig} from '@remix-run/route-config';
259
+ import {hydrogenRoutes} from '@shopify/hydrogen';
172
260
 
173
261
  export default hydrogenRoutes([
174
262
  // Your entire app reading from routes folder using Layout from layout.tsx
175
- layout("./layout.tsx", await flatRoutes()),
263
+ layout('./layout.tsx', await flatRoutes()),
176
264
  ]) satisfies RouteConfig;
177
265
  ```
178
266
 
@@ -203,7 +291,6 @@
203
291
  Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single-fetch
204
292
 
205
293
  **Note:** If you have any routes that appends (or looks for) a search param named `_data`, make sure to rename it to something else.
206
-
207
294
  1. In your `vite.config.ts`, add the single fetch future flag.
208
295
 
209
296
  ```diff
@@ -501,7 +588,6 @@
501
588
  - Remove initial redirect from product display page ([#2643](https://github.com/Shopify/hydrogen/pull/2643)) by [@scottdixon](https://github.com/scottdixon)
502
589
 
503
590
  - Optional updates for the product route and product form to handle combined listing and 2000 variant limit. ([#2659](https://github.com/Shopify/hydrogen/pull/2659)) by [@wizardlyhel](https://github.com/wizardlyhel)
504
-
505
591
  1. Update your SFAPI product query to bring in the new query fields:
506
592
 
507
593
  ```diff
@@ -763,25 +849,25 @@
763
849
  8. Update the `ProductForm` component.
764
850
 
765
851
  ```tsx
766
- import { Link, useNavigate } from "@remix-run/react";
767
- import { type MappedProductOptions } from "@shopify/hydrogen";
852
+ import {Link, useNavigate} from '@remix-run/react';
853
+ import {type MappedProductOptions} from '@shopify/hydrogen';
768
854
  import type {
769
855
  Maybe,
770
856
  ProductOptionValueSwatch,
771
- } from "@shopify/hydrogen/storefront-api-types";
772
- import { AddToCartButton } from "./AddToCartButton";
773
- import { useAside } from "./Aside";
774
- import type { ProductFragment } from "storefrontapi.generated";
857
+ } from '@shopify/hydrogen/storefront-api-types';
858
+ import {AddToCartButton} from './AddToCartButton';
859
+ import {useAside} from './Aside';
860
+ import type {ProductFragment} from 'storefrontapi.generated';
775
861
 
776
862
  export function ProductForm({
777
863
  productOptions,
778
864
  selectedVariant,
779
865
  }: {
780
866
  productOptions: MappedProductOptions[];
781
- selectedVariant: ProductFragment["selectedOrFirstAvailableVariant"];
867
+ selectedVariant: ProductFragment['selectedOrFirstAvailableVariant'];
782
868
  }) {
783
869
  const navigate = useNavigate();
784
- const { open } = useAside();
870
+ const {open} = useAside();
785
871
  return (
786
872
  <div className="product-form">
787
873
  {productOptions.map((option) => (
@@ -815,8 +901,8 @@
815
901
  to={`/products/${handle}?${variantUriQuery}`}
816
902
  style={{
817
903
  border: selected
818
- ? "1px solid black"
819
- : "1px solid transparent",
904
+ ? '1px solid black'
905
+ : '1px solid transparent',
820
906
  opacity: available ? 1 : 0.3,
821
907
  }}
822
908
  >
@@ -833,13 +919,13 @@
833
919
  <button
834
920
  type="button"
835
921
  className={`product-options-item${
836
- exists && !selected ? " link" : ""
922
+ exists && !selected ? ' link' : ''
837
923
  }`}
838
924
  key={option.name + name}
839
925
  style={{
840
926
  border: selected
841
- ? "1px solid black"
842
- : "1px solid transparent",
927
+ ? '1px solid black'
928
+ : '1px solid transparent',
843
929
  opacity: available ? 1 : 0.3,
844
930
  }}
845
931
  disabled={!exists}
@@ -863,7 +949,7 @@
863
949
  <AddToCartButton
864
950
  disabled={!selectedVariant || !selectedVariant.availableForSale}
865
951
  onClick={() => {
866
- open("cart");
952
+ open('cart');
867
953
  }}
868
954
  lines={
869
955
  selectedVariant
@@ -877,7 +963,7 @@
877
963
  : []
878
964
  }
879
965
  >
880
- {selectedVariant?.availableForSale ? "Add to cart" : "Sold out"}
966
+ {selectedVariant?.availableForSale ? 'Add to cart' : 'Sold out'}
881
967
  </AddToCartButton>
882
968
  </div>
883
969
  );
@@ -900,7 +986,7 @@
900
986
  aria-label={name}
901
987
  className="product-option-label-swatch"
902
988
  style={{
903
- backgroundColor: color || "transparent",
989
+ backgroundColor: color || 'transparent',
904
990
  }}
905
991
  >
906
992
  {!!image && <img src={image} alt={name} />}
@@ -1313,7 +1399,6 @@
1313
1399
  ### Patch Changes
1314
1400
 
1315
1401
  - Stabilize `getSitemap`, `getSitemapIndex` and implement on skeleton ([#2589](https://github.com/Shopify/hydrogen/pull/2589)) by [@juanpprieto](https://github.com/juanpprieto)
1316
-
1317
1402
  1. Update the `getSitemapIndex` at `/app/routes/[sitemap.xml].tsx`
1318
1403
 
1319
1404
  ```diff
@@ -1401,21 +1486,21 @@
1401
1486
  New `withCache.fetch` is for caching simple fetch requests. This method caches the responses if they are OK responses, and you can pass `shouldCacheResponse`, `cacheKey`, etc. to modify behavior. `data` is the consumed body of the response (we need to consume to cache it).
1402
1487
 
1403
1488
  ```ts
1404
- const withCache = createWithCache({ cache, waitUntil, request });
1489
+ const withCache = createWithCache({cache, waitUntil, request});
1405
1490
 
1406
- const { data, response } = await withCache.fetch<{ data: T; error: string }>(
1407
- "my-cms.com/api",
1491
+ const {data, response} = await withCache.fetch<{data: T; error: string}>(
1492
+ 'my-cms.com/api',
1408
1493
  {
1409
- method: "POST",
1410
- headers: { "Content-type": "application/json" },
1494
+ method: 'POST',
1495
+ headers: {'Content-type': 'application/json'},
1411
1496
  body,
1412
1497
  },
1413
1498
  {
1414
1499
  cacheStrategy: CacheLong(),
1415
1500
  // Cache if there are no data errors or a specific data that make this result not suited for caching
1416
1501
  shouldCacheResponse: (result) => !result?.error,
1417
- cacheKey: ["my-cms", body],
1418
- displayName: "My CMS query",
1502
+ cacheKey: ['my-cms', body],
1503
+ displayName: 'My CMS query',
1419
1504
  },
1420
1505
  );
1421
1506
  ```
@@ -1423,7 +1508,6 @@
1423
1508
  - [**Breaking change**] ([#2585](https://github.com/Shopify/hydrogen/pull/2585)) by [@wizardlyhel](https://github.com/wizardlyhel)
1424
1509
 
1425
1510
  Deprecate usages of `product.options.values` and use `product.options.optionValues` instead.
1426
-
1427
1511
  1. Update your product graphql query to use the new `optionValues` field.
1428
1512
 
1429
1513
  ```diff
@@ -1991,9 +2075,9 @@
1991
2075
 
1992
2076
  ```tsx
1993
2077
  // app/lib/root-data.ts
1994
- import { useMatches } from "@remix-run/react";
1995
- import type { SerializeFrom } from "@shopify/remix-oxygen";
1996
- import type { loader } from "~/root";
2078
+ import {useMatches} from '@remix-run/react';
2079
+ import type {SerializeFrom} from '@shopify/remix-oxygen';
2080
+ import type {loader} from '~/root';
1997
2081
 
1998
2082
  /**
1999
2083
  * Access the result of the root loader from a React component.
@@ -2104,7 +2188,6 @@
2104
2188
  ### Patch Changes
2105
2189
 
2106
2190
  - Improve performance of predictive search: ([#1823](https://github.com/Shopify/hydrogen/pull/1823)) by [@frandiox](https://github.com/frandiox)
2107
-
2108
2191
  - Change the request to be GET instead of POST to avoid Remix route revalidations.
2109
2192
  - Add Cache-Control headers to the response to get quicker results when typing.
2110
2193
 
@@ -2155,10 +2238,10 @@
2155
2238
  - This is an important fix to a bug with 404 routes and path-based i18n projects where some unknown routes would not properly render a 404. This fixes all new projects, but to fix existing projects, add a `($locale).tsx` route with the following contents: ([#1732](https://github.com/Shopify/hydrogen/pull/1732)) by [@blittle](https://github.com/blittle)
2156
2239
 
2157
2240
  ```ts
2158
- import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
2241
+ import {type LoaderFunctionArgs} from '@remix-run/server-runtime';
2159
2242
 
2160
- export async function loader({ params, context }: LoaderFunctionArgs) {
2161
- const { language, country } = context.storefront.i18n;
2243
+ export async function loader({params, context}: LoaderFunctionArgs) {
2244
+ const {language, country} = context.storefront.i18n;
2162
2245
 
2163
2246
  if (
2164
2247
  params.locale &&
@@ -2166,7 +2249,7 @@
2166
2249
  ) {
2167
2250
  // If the locale URL param is defined, yet we still are still at the default locale
2168
2251
  // then the the locale param must be invalid, send to the 404 page
2169
- throw new Response(null, { status: 404 });
2252
+ throw new Response(null, {status: 404});
2170
2253
  }
2171
2254
 
2172
2255
  return null;
@@ -2222,11 +2305,11 @@
2222
2305
  ```yaml
2223
2306
  projects:
2224
2307
  default:
2225
- schema: "node_modules/@shopify/hydrogen/storefront.schema.json"
2308
+ schema: 'node_modules/@shopify/hydrogen/storefront.schema.json'
2226
2309
  documents:
2227
- - "!*.d.ts"
2228
- - "*.{ts,tsx,js,jsx}"
2229
- - "app/**/*.{ts,tsx,js,jsx}"
2310
+ - '!*.d.ts'
2311
+ - '*.{ts,tsx,js,jsx}'
2312
+ - 'app/**/*.{ts,tsx,js,jsx}'
2230
2313
  ```
2231
2314
 
2232
2315
  - Improve resiliency of `HydrogenSession` ([#1583](https://github.com/Shopify/hydrogen/pull/1583)) by [@blittle](https://github.com/blittle)
@@ -2324,7 +2407,6 @@
2324
2407
  ### Major Changes
2325
2408
 
2326
2409
  - The Storefront API 2023-10 now returns menu item URLs that include the `primaryDomainUrl`, instead of defaulting to the Shopify store ID URL (example.myshopify.com). The skeleton template requires changes to check for the `primaryDomainUrl`: by [@blittle](https://github.com/blittle)
2327
-
2328
2410
  1. Update the `HeaderMenu` component to accept a `primaryDomainUrl` and include
2329
2411
  it in the internal url check
2330
2412
 
@@ -2441,8 +2523,8 @@
2441
2523
  ```ts
2442
2524
  // root.tsx
2443
2525
 
2444
- import { useMatches } from "@remix-run/react";
2445
- import { type SerializeFrom } from "@shopify/remix-oxygen";
2526
+ import {useMatches} from '@remix-run/react';
2527
+ import {type SerializeFrom} from '@shopify/remix-oxygen';
2446
2528
 
2447
2529
  export const useRootLoaderData = () => {
2448
2530
  const [root] = useMatches();
@@ -1,4 +1,4 @@
1
- import { type FetcherWithComponents } from 'react-router';
1
+ import {type FetcherWithComponents} from 'react-router';
2
2
  import {CartForm, type OptimisticCartLineInput} from '@shopify/hydrogen';
3
3
 
4
4
  export function AddToCartButton({
@@ -2,7 +2,7 @@ import type {CartLineUpdateInput} from '@shopify/hydrogen/storefront-api-types';
2
2
  import type {CartLayout} from '~/components/CartMain';
3
3
  import {CartForm, Image, type OptimisticCartLine} from '@shopify/hydrogen';
4
4
  import {useVariantUrl} from '~/lib/variants';
5
- import { Link } from 'react-router';
5
+ import {Link} from 'react-router';
6
6
  import {ProductPrice} from './ProductPrice';
7
7
  import {useAside} from './Aside';
8
8
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
@@ -1,5 +1,5 @@
1
1
  import {useOptimisticCart} from '@shopify/hydrogen';
2
- import { Link } from 'react-router';
2
+ import {Link} from 'react-router';
3
3
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
4
4
  import {useAside} from '~/components/Aside';
5
5
  import {CartLineItem} from '~/components/CartLineItem';
@@ -1,8 +1,9 @@
1
1
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
2
2
  import type {CartLayout} from '~/components/CartMain';
3
3
  import {CartForm, Money, type OptimisticCart} from '@shopify/hydrogen';
4
- import {useRef} from 'react';
5
- import { FetcherWithComponents } from 'react-router';
4
+ import {useEffect, useRef} from 'react';
5
+ import {useFetcher} from 'react-router';
6
+ import type {FetcherWithComponents} from 'react-router';
6
7
 
7
8
  type CartSummaryProps = {
8
9
  cart: OptimisticCart<CartApiQueryFragment | null>;
@@ -19,19 +20,20 @@ export function CartSummary({cart, layout}: CartSummaryProps) {
19
20
  <dl className="cart-subtotal">
20
21
  <dt>Subtotal</dt>
21
22
  <dd>
22
- {cart.cost?.subtotalAmount?.amount ? (
23
- <Money data={cart.cost?.subtotalAmount} />
23
+ {cart?.cost?.subtotalAmount?.amount ? (
24
+ <Money data={cart?.cost?.subtotalAmount} />
24
25
  ) : (
25
26
  '-'
26
27
  )}
27
28
  </dd>
28
29
  </dl>
29
- <CartDiscounts discountCodes={cart.discountCodes} />
30
- <CartGiftCard giftCardCodes={cart.appliedGiftCards} />
31
- <CartCheckoutActions checkoutUrl={cart.checkoutUrl} />
30
+ <CartDiscounts discountCodes={cart?.discountCodes} />
31
+ <CartGiftCard giftCardCodes={cart?.appliedGiftCards} />
32
+ <CartCheckoutActions checkoutUrl={cart?.checkoutUrl} />
32
33
  </div>
33
34
  );
34
35
  }
36
+
35
37
  function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
36
38
  if (!checkoutUrl) return null;
37
39
 
@@ -110,41 +112,47 @@ function CartGiftCard({
110
112
  }) {
111
113
  const appliedGiftCardCodes = useRef<string[]>([]);
112
114
  const giftCardCodeInput = useRef<HTMLInputElement>(null);
113
- const codes: string[] =
114
- giftCardCodes?.map(({lastCharacters}) => `***${lastCharacters}`) || [];
115
+ const giftCardAddFetcher = useFetcher({key: 'gift-card-add'});
116
+
117
+ // Clear the gift card code input after the gift card is added
118
+ useEffect(() => {
119
+ if (giftCardAddFetcher.data) {
120
+ giftCardCodeInput.current!.value = '';
121
+ }
122
+ }, [giftCardAddFetcher.data]);
115
123
 
116
124
  function saveAppliedCode(code: string) {
117
125
  const formattedCode = code.replace(/\s/g, ''); // Remove spaces
118
126
  if (!appliedGiftCardCodes.current.includes(formattedCode)) {
119
127
  appliedGiftCardCodes.current.push(formattedCode);
120
128
  }
121
- giftCardCodeInput.current!.value = '';
122
- }
123
-
124
- function removeAppliedCode() {
125
- appliedGiftCardCodes.current = [];
126
129
  }
127
130
 
128
131
  return (
129
132
  <div>
130
- {/* Have existing gift card applied, display it with a remove option */}
131
- <dl hidden={!codes.length}>
132
- <div>
133
+ {/* Display applied gift cards with individual remove buttons */}
134
+ {giftCardCodes && giftCardCodes.length > 0 && (
135
+ <dl>
133
136
  <dt>Applied Gift Card(s)</dt>
134
- <UpdateGiftCardForm>
135
- <div className="cart-discount">
136
- <code>{codes?.join(', ')}</code>
137
- &nbsp;
138
- <button onSubmit={() => removeAppliedCode}>Remove</button>
139
- </div>
140
- </UpdateGiftCardForm>
141
- </div>
142
- </dl>
137
+ {giftCardCodes.map((giftCard) => (
138
+ <RemoveGiftCardForm key={giftCard.id} giftCardId={giftCard.id}>
139
+ <div className="cart-discount">
140
+ <code>***{giftCard.lastCharacters}</code>
141
+ &nbsp;
142
+ <Money data={giftCard.amountUsed} />
143
+ &nbsp;
144
+ <button type="submit">Remove</button>
145
+ </div>
146
+ </RemoveGiftCardForm>
147
+ ))}
148
+ </dl>
149
+ )}
143
150
 
144
- {/* Show an input to apply a discount */}
151
+ {/* Show an input to apply a gift card */}
145
152
  <UpdateGiftCardForm
146
153
  giftCardCodes={appliedGiftCardCodes.current}
147
154
  saveAppliedCode={saveAppliedCode}
155
+ fetcherKey="gift-card-add"
148
156
  >
149
157
  <div>
150
158
  <input
@@ -154,7 +162,9 @@ function CartGiftCard({
154
162
  ref={giftCardCodeInput}
155
163
  />
156
164
  &nbsp;
157
- <button type="submit">Apply</button>
165
+ <button type="submit" disabled={giftCardAddFetcher.state !== 'idle'}>
166
+ Apply
167
+ </button>
158
168
  </div>
159
169
  </UpdateGiftCardForm>
160
170
  </div>
@@ -164,15 +174,17 @@ function CartGiftCard({
164
174
  function UpdateGiftCardForm({
165
175
  giftCardCodes,
166
176
  saveAppliedCode,
177
+ fetcherKey,
167
178
  children,
168
179
  }: {
169
180
  giftCardCodes?: string[];
170
181
  saveAppliedCode?: (code: string) => void;
171
- removeAppliedCode?: () => void;
182
+ fetcherKey?: string;
172
183
  children: React.ReactNode;
173
184
  }) {
174
185
  return (
175
186
  <CartForm
187
+ fetcherKey={fetcherKey}
176
188
  route="/cart"
177
189
  action={CartForm.ACTIONS.GiftCardCodesUpdate}
178
190
  inputs={{
@@ -189,3 +201,24 @@ function UpdateGiftCardForm({
189
201
  </CartForm>
190
202
  );
191
203
  }
204
+
205
+ function RemoveGiftCardForm({
206
+ giftCardId,
207
+ children,
208
+ }: {
209
+ giftCardId: string;
210
+ children: React.ReactNode;
211
+ }) {
212
+ return (
213
+ <CartForm
214
+ route="/cart"
215
+ action={CartForm.ACTIONS.GiftCardCodesRemove}
216
+ inputs={{
217
+ giftCardCodes: [giftCardId],
218
+ }}
219
+ >
220
+ {children}
221
+ </CartForm>
222
+ );
223
+ }
224
+
@@ -1,5 +1,5 @@
1
1
  import {Suspense} from 'react';
2
- import { Await, NavLink, useAsyncValue } from 'react-router';
2
+ import {Await, NavLink, useAsyncValue} from 'react-router';
3
3
  import {
4
4
  type CartViewPayload,
5
5
  useAnalytics,
@@ -1,4 +1,4 @@
1
- import { Await, Link } from 'react-router';
1
+ import {Await, Link} from 'react-router';
2
2
  import {Suspense, useId} from 'react';
3
3
  import type {
4
4
  CartApiQueryFragment,
@@ -1,4 +1,4 @@
1
- import { Link, useNavigate } from 'react-router';
1
+ import {Link, useNavigate} from 'react-router';
2
2
  import {type MappedProductOptions} from '@shopify/hydrogen';
3
3
  import type {
4
4
  Maybe,
@@ -84,7 +84,7 @@ export function ProductForm({
84
84
  disabled={!exists}
85
85
  onClick={() => {
86
86
  if (!selected) {
87
- navigate(`?${variantUriQuery}`, {
87
+ void navigate(`?${variantUriQuery}`, {
88
88
  replace: true,
89
89
  preventScrollReset: true,
90
90
  });
@@ -1,5 +1,5 @@
1
1
  import {useRef, useEffect} from 'react';
2
- import { Form, type FormProps } from 'react-router';
2
+ import {Form, type FormProps} from 'react-router';
3
3
 
4
4
  type SearchFormProps = Omit<FormProps, 'children'> & {
5
5
  children: (args: {
@@ -1,4 +1,9 @@
1
- import { useFetcher, useNavigate, type FormProps, type Fetcher } from 'react-router';
1
+ import {
2
+ useFetcher,
3
+ useNavigate,
4
+ type FormProps,
5
+ type Fetcher,
6
+ } from 'react-router';
2
7
  import React, {useRef, useEffect} from 'react';
3
8
  import type {PredictiveSearchReturn} from '~/lib/search';
4
9
  import {useAside} from './Aside';
@@ -41,13 +46,13 @@ export function SearchFormPredictive({
41
46
  /** Navigate to the search page with the current input value */
42
47
  function goToSearch() {
43
48
  const term = inputRef?.current?.value;
44
- navigate(SEARCH_ENDPOINT + (term ? `?q=${term}` : ''));
49
+ void navigate(SEARCH_ENDPOINT + (term ? `?q=${term}` : ''));
45
50
  aside.close();
46
51
  }
47
52
 
48
53
  /** Fetch search results based on the input value */
49
54
  function fetchResults(event: React.ChangeEvent<HTMLInputElement>) {
50
- fetcher.submit(
55
+ void fetcher.submit(
51
56
  {q: event.target.value || '', limit: 5, predictive: true},
52
57
  {method: 'GET', action: SEARCH_ENDPOINT},
53
58
  );
@@ -1,4 +1,4 @@
1
- import { Link } from 'react-router';
1
+ import {Link} from 'react-router';
2
2
  import {Image, Money, Pagination} from '@shopify/hydrogen';
3
3
  import {urlWithTrackingParams, type RegularSearchReturn} from '~/lib/search';
4
4
 
@@ -120,19 +120,11 @@ function SearchResultsProducts({
120
120
  <div className="search-results-item" key={product.id}>
121
121
  <Link prefetch="intent" to={productUrl}>
122
122
  {image && (
123
- <Image
124
- data={image}
125
- alt={product.title}
126
- width={50}
127
- />
123
+ <Image data={image} alt={product.title} width={50} />
128
124
  )}
129
125
  <div>
130
126
  <p>{product.title}</p>
131
- <small>
132
- {price &&
133
- <Money data={price} />
134
- }
135
- </small>
127
+ <small>{price && <Money data={price} />}</small>
136
128
  </div>
137
129
  </Link>
138
130
  </div>