2025.7.0 Upgrade Help #3248
Replies: 2 comments 2 replies
-
|
How do we handle i18n? Before I passed i18n in createStorefrontClient But now it's also passed to createHydrogenContext Do we have to pass it to both?
|
Beta Was this translation helpful? Give feedback.
-
|
Hey @juanpprieto ! This looks like a bug in either in 2025.7.0 where the Hydrogen plugin's internal code is being processed incorrectly, or in vite.config.js config, or in ESLint. --> I've tried copy/pasting vite.config + eslint config from the newly created tempalte of 2025.07, facing the same issue. SummaryThe dev server fails because esbuild tries to bundle Node.js built-in modules (url, path, fs/promises) from Hydrogen's internal code during dependency pre-bundling. Current Status
The outputs (I've tried workaround but get different issues, related to the same problem) 9:45:49 AM [react-router] generated types
9:45:50 AM [vite] (ssr) warning: Automatically externalized node built-in module "url" imported from "node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js". Consider adding it to environments.ssr.external if it is intended.
Plugin: vite:resolve
9:45:50 AM [vite] (ssr) warning: Automatically externalized node built-in module "path" imported from "node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js". Consider adding it to environments.ssr.external if it is intended.
Plugin: vite:resolve
9:45:50 AM [vite] (ssr) warning: Automatically externalized node built-in module "fs/promises" imported from "node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js". Consider adding it to environments.ssr.external if it is intended.
Plugin: vite:resolve
✘ [ERROR] Cannot read file: /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/fs/promises
node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:3:24:
3 │ import { readdir } from 'fs/promises';
╵ ~~~~~~~~~~~~~
✘ [ERROR] Cannot read file: /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/path
node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:2:17:
2 │ import path from 'path';
╵ ~~~~~~
✘ [ERROR] Cannot read file: /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/url
node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:1:30:
1 │ import { fileURLToPath } from 'url';
╵ ~~~~~
╭─ error ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Build failed with 3 errors: │
│ node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:1:30: ERROR: Cannot read file: /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/url │
│ node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:2:17: ERROR: Cannot read file: /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/path │
│ node_modules/@shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:3:24: ERROR: Cannot read file: │
│ /Users/XXX/Documents/WIP/XXX/Shop/XXX-storefront/fs/promises │
│ │
│ To investigate the issue, examine this stack trace: │
│ at node_modules/ (shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:1) │
│ at node_modules/ (shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:2) │
│ at node_modules/ (shopify/hydrogen/dist/development/get-virtual-routes-6PVSMJPH.js:3) │
│ at failureErrorWithLog (esbuild/lib/main.js:1463) │
│ at (esbuild/lib/main.js:924) │
│ at (esbuild/lib/main.js:1341) │
│ at processTicksAndRejections (node:internal/process/task_queues:95) │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Any idea how to fix the upgrade ? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Depending on your starting version, you may see conflicts when running
h2 upgrade. If that happens, follow these manual upgrade stepsManual Upgrade Steps
1. Uninstall old dependencies:
2. Install the latest dependencies:
3. If issues persist: (optional)
3.1 Manually update
package.jsonhydrogen related dependencies to the current skeleton (included bellow)3.2 Delete
package-lock.json3.3 Install updated version with run
npm install.Hydrogen skeleton
2025.7.0dependencies.{ "scripts": { "build": "shopify hydrogen build --codegen", "dev": "shopify hydrogen dev --codegen", "preview": "shopify hydrogen preview --build", "lint": "eslint --no-error-on-unmatched-pattern .", "typecheck": "react-router typegen && tsc --noEmit", "codegen": "shopify hydrogen codegen && react-router typegen" }, "dependencies": { "@shopify/hydrogen": "2025.7.0", "react": "18.3.1", "react-dom": "18.3.1", "react-router": "7.9.2", "react-router-dom": "7.9.2" }, "devDependencies": { "@shopify/cli": "3.85.4", "@shopify/hydrogen-codegen": "^0.3.3", "@shopify/mini-oxygen": "^4.0.0", "@shopify/oxygen-workers-types": "^4.1.6", "@graphql-codegen/cli": "5.0.2", "@react-router/dev": "7.9.2", "@react-router/fs-routes": "7.9.2", } }Hydrogen upgrade guide: 2025.4.1 to 2025.7.0
Note
If you are on
2025.5.0some steps may not apply.Breaking changes
Migrate to React Router 7.9.x #3141
Step: 1. Run the automated migration codemod #3141
Step: 2. Create react-router.config.ts with hydrogenPreset #3141
Step: 3. Update vite.config.ts to use React Router plugin #3141
// vite.config.ts import {defineConfig} from 'vite'; - import {vitePlugin as remix} from '@remix-run/dev'; + import {reactRouter} from '@react-router/dev/vite'; import {hydrogen} from '@shopify/hydrogen/vite'; import {oxygen} from '@shopify/mini-oxygen/vite'; + import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [ + hydrogen(), + oxygen(), + reactRouter(), + tsconfigPaths() - remix({ - presets: [hydrogen.preset()], - }), ], });Step: 4. Update tsconfig.json for React Router type generation #3141
// tsconfig.json { "include": [ "env.d.ts", "app/**/*.ts", "app/**/*.tsx", + "app/**/*.d.ts", + "*.ts", + "*.tsx", + "*.d.ts", + ".graphqlrc.ts", + ".react-router/types/**/*" - "**/*.ts", - "**/*.tsx" ], "compilerOptions": { "types": [ "@shopify/oxygen-workers-types", + "react-router", + "@shopify/hydrogen/react-router-types", + "vite/client" - "@remix-run/node", - "vite/client" ], + "rootDirs": [".", "./.react-router/types"], "baseUrl": ".", "paths": { "~/*": ["app/*"] } } }Step: 5. Create app/lib/context.ts with createHydrogenRouterContext #3141
Step: 6. Update server.ts to use createHydrogenRouterContext #3141
Step: 7. Update entry.server.tsx with new context types #3141
Step: 8. Update entry.client.tsx with NonceProvider and HydratedRouter #3141
Step: 9. Update @shopify/remix-oxygen imports in route files #3141
Step: 10. Update @remix-run/react imports in route files #3141
Step: 11. Add React Router 7 route type imports #3141
+ import type {Route} from "./+types/route-name";Step: 12. Add .react-router to .gitignore #3141
Step: 13. Update package.json scripts to use react-router typegen #3141
Step: 14. Verify your app starts and builds correctly #3141
Features
Add countryCode parameter to Customer Account API methods #3148
Add countryCode parameter to Customer Account API method calls
// app/routes/account_.login.tsx (example) export async function loader({request, context}: Route.LoaderArgs) { return context.customerAccount.login({ + countryCode: context.customerAccount.i18n.country, }); } // The countryCode parameter is now available on all Customer Account API methods // and can be passed from context.customerAccount.i18n.countryRemove individual gift cards from cart #3128
Step: 1. Add GiftCardCodesRemove case to cart action handler #3128
// app/routes/cart.tsx export async function action({request, context}: Route.ActionArgs) { const {cart} = context; const formData = await request.formData(); const {action, inputs} = CartForm.getFormInput(formData); switch (action) { // ... existing cases ... + case CartForm.ACTIONS.GiftCardCodesRemove: { + const appliedGiftCardIds = inputs.giftCardCodes as string[]; + result = await cart.removeGiftCardCodes(appliedGiftCardIds); + break; + } } }Step: 2. Add RemoveGiftCardForm component #3128
Step: 3. Update CartGiftCard to display gift cards with remove buttons #3128
// app/components/CartSummary.tsx function CartGiftCard({giftCardCodes}: {...}) { return ( <div> + {giftCardCodes && giftCardCodes.length > 0 && ( + <dl> + <dt>Applied Gift Card(s)</dt> + {giftCardCodes.map((giftCard) => ( + <RemoveGiftCardForm key={giftCard.id} giftCardId={giftCard.id}> + <div className="cart-discount"> + <code>***{giftCard.lastCharacters}</code> + <Money data={giftCard.amountUsed} /> + <button type="submit">Remove</button> + </div> + </RemoveGiftCardForm> + ))} + </dl> + )} </div> ); }Filter customer orders by number and confirmation #3125
Step: 1. Create app/lib/orderFilters.ts utility #3125
Step: 2. Update loader to parse filters and build search query #3125
Step: 3. Add OrderSearchForm component #3125
Step: 4. Update Orders component to use filters and add EmptyOrders #3125
// app/routes/account.orders._index.tsx export default function Orders() { - const {customer} = useLoaderData<OrdersLoaderData>(); + const {customer, filters} = useLoaderData<OrdersLoaderData>(); const {orders} = customer; return ( <div className="orders"> + <OrderSearchForm currentFilters={filters} /> - <OrdersTable orders={orders} /> + <OrdersTable orders={orders} filters={filters} /> </div> ); } -function OrdersTable({orders}: {orders: CustomerOrdersFragment['orders']}) { +function OrdersTable({orders, filters}: {orders: CustomerOrdersFragment['orders']; filters: OrderFilterParams}) { + const hasFilters = !!(filters.name || filters.confirmationNumber); + return ( <div> {orders?.nodes.length ? ( <PaginatedResourceSection connection={orders}> {({node: order}) => <OrderItem key={order.id} order={order} />} </PaginatedResourceSection> ) : ( - <p>You haven't placed any orders yet.</p> + <EmptyOrders hasFilters={hasFilters} /> )} </div> ); } +function EmptyOrders({hasFilters}: {hasFilters?: boolean}) { + return hasFilters ? ( + <p>No orders found matching your search. <Link to="/account/orders">Clear filters</Link></p> + ) : ( + <p>You haven't placed any orders yet.</p> + ); +}Get order fulfillment status from Customer Account API #3039-fulfillment
Add fulfillmentStatus field to Order fragment
// app/graphql/customer-account/CustomerOrderQuery.ts fragment Order on Order { id name confirmationNumber statusPageUrl + fulfillmentStatus processedAt fulfillments(first: 1) { nodes { status } } }Use language context in Customer Account API mutations #3039-incontext
Add @incontext directive to all Customer Account API operations
Defer non-critical fields with GraphQL @defer directive #2993
Use @defer directive in Storefront API queries
Fixes
Use stable Customer Account API development flag #3082-flag
Update command to use stable flag
Add TypeScript ESLint rules for promise handling #3146
Add promise handling rules to ESLint config
// eslint.config.js export default tseslint.config( { rules: { // ... existing rules ... + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-misused-promises': 'error', }, }, ); // These rules prevent unhandled promises and promise misuse // Helps avoid 'The script will never generate a response' errors on Oxygen/Cloudflare WorkersBeta Was this translation helpful? Give feedback.
All reactions