- Bump to get new cli package version by @wizardlyhel
-
Stabilize
getSitemap
,getSitemapIndex
and implement on skeleton (#2589) by @juanpprieto- Update the
getSitemapIndex
at/app/routes/[sitemap.xml].tsx
- import {unstable__getSitemapIndex as getSitemapIndex} from '@shopify/hydrogen'; + import {getSitemapIndex} from '@shopify/hydrogen';
- Update the
getSitemap
at/app/routes/sitemap.$type.$page[.xml].tsx
- import {unstable__getSitemap as getSitemap} from '@shopify/hydrogen'; + import {getSitemap} from '@shopify/hydrogen';
For a reference implementation please see the skeleton template sitemap routes
- Update the
-
[Breaking change] (#2588) by @wizardlyhel
Set up Customer Privacy without the Shopify's cookie banner by default.
If you are using Shopify's cookie banner to handle user consent in your app, you need to set
withPrivacyBanner: true
to the consent config. Without this update, the Shopify cookie banner will not appear.return defer({ ... consent: { checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN, storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN, + withPrivacyBanner: true, // localize the privacy banner country: args.context.storefront.i18n.country, language: args.context.storefront.i18n.language, }, });
-
Update to 2024-10 SFAPI (#2570) by @wizardlyhel
-
[Breaking change] (#2546) by @frandiox
Update
createWithCache
to make it harder to accidentally cache undesired results.request
is now mandatory prop when initializingcreateWithCache
.// server.ts export default { async fetch( request: Request, env: Env, executionContext: ExecutionContext, ): Promise<Response> { try { // ... - const withCache = createWithCache({cache, waitUntil}); + const withCache = createWithCache({cache, waitUntil, request});
createWithCache
now returns an object with two utility functions:withCache.run
andwithCache.fetch
. Both have a new propshouldCacheResult
that must be defined.The original
withCache
callback function is nowwithCache.run
. This is useful to run multiple fetch calls and merge their responses, or run any arbitrary code. It caches anything you return, but you can throw if you don't want to cache anything.const withCache = createWithCache({cache, waitUntil, request}); const fetchMyCMS = (query) => { - return withCache(['my-cms', query], CacheLong(), async (params) => { + return withCache.run({ + cacheKey: ['my-cms', query], + cacheStrategy: CacheLong(), + // Cache if there are no data errors or a specific data that make this result not suited for caching + shouldCacheResult: (result) => !result?.errors, + }, async(params) => { const response = await fetch('my-cms.com/api', { method: 'POST', body: query, }); if (!response.ok) throw new Error(response.statusText); const {data, error} = await response.json(); if (error || !data) throw new Error(error ?? 'Missing data'); params.addDebugData({displayName: 'My CMS query', response}); return data; }); };
New
withCache.fetch
is for caching simple fetch requests. This method caches the responses if they are OK responses, and you can passshouldCacheResponse
,cacheKey
, etc. to modify behavior.data
is the consumed body of the response (we need to consume to cache it).const withCache = createWithCache({cache, waitUntil, request}); const {data, response} = await withCache.fetch<{data: T; error: string}>( 'my-cms.com/api', { method: 'POST', headers: {'Content-type': 'application/json'}, body, }, { cacheStrategy: CacheLong(), // Cache if there are no data errors or a specific data that make this result not suited for caching shouldCacheResponse: (result) => !result?.error, cacheKey: ['my-cms', body], displayName: 'My CMS query', }, );
-
[Breaking change] (#2585) by @wizardlyhel
Deprecate usages of
product.options.values
and useproduct.options.optionValues
instead.- Update your product graphql query to use the new
optionValues
field.
const PRODUCT_FRAGMENT = `#graphql fragment Product on Product { id title options { name - values + optionValues { + name + } }
- Update your
<VariantSelector>
to use the newoptionValues
field.
<VariantSelector handle={product.handle} - options={product.options.filter((option) => option.values.length > 1)} + options={product.options.filter((option) => option.optionValues.length > 1)} variants={variants} >
- Update your product graphql query to use the new
-
Updated dependencies [
d97cd56e
,809c9f3d
,8c89f298
,a253ef97
,84a66b1e
,227035e7
,ac12293c
,c7c9f2eb
,76cd4f9b
,8337e534
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Use HTML datalist element for query suggestions for autocomplete experience (#2506) by @frontsideair
-
Bump cli packages version (#2592) by @wizardlyhel
-
Updated dependencies [
e963389d
,d08d8c37
]:- @shopify/[email protected]
- Updated dependencies [
f3363030
,bb5b0979
]:- @shopify/[email protected]
- @shopify/[email protected]
- Updated dependencies [
39f8f8fd
]:- @shopify/[email protected]
- Updated dependencies [
d0ff37a9
]:- @shopify/[email protected]
-
Update Shopify CLI and cli-kit dependencies to 3.66.1 (#2512) by @frandiox
-
createCartHandler supplies updateGiftCardCodes method (#2298) by @wizardlyhel
-
Fix menu links in side panel not working on mobile devices (#2450) by @wizardlyhel
// /app/components/Header.tsx export function HeaderMenu({ menu, primaryDomainUrl, viewport, publicStoreDomain, }: { menu: HeaderProps['header']['menu']; primaryDomainUrl: HeaderProps['header']['shop']['primaryDomain']['url']; viewport: Viewport; publicStoreDomain: HeaderProps['publicStoreDomain']; }) { const className = `header-menu-${viewport}`; + const {close} = useAside(); - function closeAside(event: React.MouseEvent<HTMLAnchorElement>) { - if (viewport === 'mobile') { - event.preventDefault(); - window.location.href = event.currentTarget.href; - } - } return ( <nav className={className} role="navigation"> {viewport === 'mobile' && ( <NavLink end - onClick={closeAside} + onClick={close} prefetch="intent" style={activeLinkStyle} to="/" > Home </NavLink> )} {(menu || FALLBACK_HEADER_MENU).items.map((item) => { if (!item.url) return null; // if the url is internal, we strip the domain const url = item.url.includes('myshopify.com') || item.url.includes(publicStoreDomain) || item.url.includes(primaryDomainUrl) ? new URL(item.url).pathname : item.url; return ( <NavLink className="header-menu-item" end key={item.id} - onClick={closeAside} + onClick={close} prefetch="intent" style={activeLinkStyle} to={url} > {item.title} </NavLink> ); })} </nav> ); }
-
Add localization support to consent privacy banner (#2457) by @juanpprieto
-
Updated dependencies [
d633e49a
,1b217cd6
,d929b561
,664a09d5
,0c1e511d
,eefa8203
]:- @shopify/[email protected]
- @shopify/[email protected]
- Updated dependencies [
b0d3bc06
]:- @shopify/[email protected]
-
Search & Predictive Search improvements (#2363) by @juanpprieto
-
// in app/lib/context import {createHydrogenContext} from '@shopify/hydrogen'; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext, ) { const hydrogenContext = createHydrogenContext({ env, request, cache, waitUntil, session, i18n: {language: 'EN', country: 'US'}, cart: { queryFragment: CART_QUERY_FRAGMENT, }, // ensure to overwrite any options that is not using the default values from your server.ts }); return { ...hydrogenContext, // declare additional Remix loader context }; }
- Use
createAppLoadContext
method in server.ts Ensure to overwrite any options that is not using the default values increateHydrogenContext
.
// in server.ts - import { - createCartHandler, - createStorefrontClient, - createCustomerAccountClient, - } from '@shopify/hydrogen'; + import {createAppLoadContext} from '~/lib/context'; export default { async fetch( request: Request, env: Env, executionContext: ExecutionContext, ): Promise<Response> { - const {storefront} = createStorefrontClient( - ... - ); - const customerAccount = createCustomerAccountClient( - ... - ); - const cart = createCartHandler( - ... - ); + const appLoadContext = await createAppLoadContext( + request, + env, + executionContext, + ); /** * Create a Remix request handler and pass * Hydrogen's Storefront client to the loader context. */ const handleRequest = createRequestHandler({ build: remixBuild, mode: process.env.NODE_ENV, - getLoadContext: (): AppLoadContext => ({ - session, - storefront, - customerAccount, - cart, - env, - waitUntil, - }), + getLoadContext: () => appLoadContext, }); }
- Use infer type for AppLoadContext in env.d.ts
// in env.d.ts + import type {createAppLoadContext} from '~/lib/context'; + interface AppLoadContext extends Awaited<ReturnType<typeof createAppLoadContext>> { - interface AppLoadContext { - env: Env; - cart: HydrogenCart; - storefront: Storefront; - customerAccount: CustomerAccount; - session: AppSession; - waitUntil: ExecutionContext['waitUntil']; }
- Use
-
Use type
HydrogenEnv
for all the env.d.ts (#2333) by @michenly// in env.d.ts + import type {HydrogenEnv} from '@shopify/hydrogen'; + interface Env extends HydrogenEnv {} - interface Env { - SESSION_SECRET: string; - PUBLIC_STOREFRONT_API_TOKEN: string; - PRIVATE_STOREFRONT_API_TOKEN: string; - PUBLIC_STORE_DOMAIN: string; - PUBLIC_STOREFRONT_ID: string; - PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string; - PUBLIC_CUSTOMER_ACCOUNT_API_URL: string; - PUBLIC_CHECKOUT_DOMAIN: string; - }
-
Add a hydration check for google web cache. This prevents an infinite redirect when viewing the cached version of a hydrogen site on Google. (#2334) by @blittle
Update your entry.server.jsx file to include this check:
+ if (!window.location.origin.includes("webcache.googleusercontent.com")) { startTransition(() => { hydrateRoot( document, <StrictMode> <RemixBrowser /> </StrictMode> ); }); + }
-
Updated dependencies [
a2d9acf9
,c0d7d917
,b09e9a4c
,c204eacf
,bf4e3d3c
,20a8e63b
,6e5d8ea7
,7c4f67a6
,dfb9be77
,31ea19e8
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]
- Updated dependencies [
150854ed
]:- @shopify/[email protected]
-
Changed the GraphQL config file format to be TS/JS instead of YAML. (#2311) by @frandiox
-
Updated dependencies [
18ea233c
,8b2322d7
]:- @shopify/[email protected]
-
Update
@shopify/oxygen-workers-types
to fix issues on Windows. (#2252) by @michenly -
[Breaking change] (#2113) by @blittle
Previously the
VariantSelector
component would filter out options that only had one value. This is undesireable for some apps. We've removed that filter, if you'd like to retain the existing functionality, simply filter the options prop before it is passed to theVariantSelector
component:<VariantSelector handle={product.handle} + options={product.options.filter((option) => option.values.length > 1)} - options={product.options} variants={variants}> </VariantSelector>
Fixes #1198
-
Update root to use Remix's Layout Export pattern and eliminate the use of
useLoaderData
in root. (#2292) by @michenlyThe diff below showcase how you can make this refactor in existing application.
import { Outlet, - useLoaderData, + useRouteLoaderData, } from '@remix-run/react'; -import {Layout} from '~/components/Layout'; +import {PageLayout} from '~/components/PageLayout'; -export default function App() { +export function Layout({children}: {children?: React.ReactNode}) { const nonce = useNonce(); - const data = useLoaderData<typeof loader>(); + const data = useRouteLoaderData<typeof loader>('root'); return ( <html> ... <body> - <Layout {...data}> - <Outlet /> - </Layout> + {data? ( + <PageLayout {...data}>{children}</PageLayout> + ) : ( + children + )} </body> </html> ); } +export default function App() { + return <Outlet />; +} export function ErrorBoundary() { - const rootData = useLoaderData<typeof loader>(); return ( - <html> - ... - <body> - <Layout {...rootData}> - <div className="route-error"> - <h1>Error</h1> - ... - </div> - </Layout> - </body> - </html> + <div className="route-error"> + <h1>Error</h1> + ... + </div> ); }
-
Refactor the cart and product form components (#2132) by @blittle
-
Remove manual setting of session in headers and recommend setting it in server after response is created. (#2137) by @michenly
Step 1: Add
isPending
implementation in session// in app/lib/session.ts export class AppSession implements HydrogenSession { + public isPending = false; get unset() { + this.isPending = true; return this.#session.unset; } get set() { + this.isPending = true; return this.#session.set; } commit() { + this.isPending = false; return this.#sessionStorage.commitSession(this.#session); } }
Step 2: update response header if
session.isPending
is true// in server.ts export default { async fetch(request: Request): Promise<Response> { try { const response = await handleRequest(request); + if (session.isPending) { + response.headers.set('Set-Cookie', await session.commit()); + } return response; } catch (error) { ... } }, };
Step 3: remove setting cookie with session.commit() in routes
// in route files export async function loader({context}: LoaderFunctionArgs) { return json({}, - { - headers: { - 'Set-Cookie': await context.session.commit(), - }, }, ); }
-
Moved
@shopify/cli
fromdependencies
todevDependencies
. (#2312) by @frandiox -
The
@shopify/cli
package now bundles the@shopify/cli-hydrogen
plugin. Therefore, you can now remove the latter from your local dependencies: (#2306) by @frandiox"@shopify/cli": "3.64.0", - "@shopify/cli-hydrogen": "^8.1.1", "@shopify/hydrogen": "2024.7.0",
-
Updated dependencies [
a0e84d76
,426bb390
,4337200c
,710625c7
,8b9c726d
,10a419bf
,6a6278bb
,66236ca6
,dcbd0bbf
,a5e03e2a
,c2690653
,54c2f7ad
,4337200c
,e96b332b
,f3065371
,6cd5554b
,9eb60d73
,e432533e
,de3f70be
,83cb96f4
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]
-
<Analytics>
anduseAnalytics
are now stable. (#2141) by @wizardlyhel -
Update the skeleton template to use React state in the aside dialogs (#2088) by @blittle
-
Updated dependencies [
fe82119f
,32d4c33e
,8eea75ec
,27e51abf
,f29c9085
,7b838beb
,d702aec2
,ca4cf045
,5a554b2e
,27e51abf
,5d6465b3
,608389d6
,9dfd1cfe
,7def3e9f
,65239c76
,ca7f2888
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Add JSdoc to
getSelectedProductOptions
utility and cleanup the skeleton implementation (#2089) by @juanpprieto -
Updated dependencies [
286589ee
,6f5061d9
,ae262b61
,2c11ca3b
,b70f9c2c
,17528db1
,58ea9bb0
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Update
@shopify/cli
dependency to avoid React version mismatches in your project: (#2059) by @frandiox"dependencies": { ... - "@shopify/cli": "3.58.0", + "@shopify/cli": "3.59.2", ... }
-
Updated dependencies [
d2bc720b
]:- @shopify/[email protected]
-
Pin React dependency to 18.2.0 to avoid mismatches. (#2051) by @frandiox
-
Updated dependencies [
9c36c8a5
]:- @shopify/[email protected]
-
Stop inlining the favicon in base64 to avoid issues with the Content-Security-Policy. In
vite.config.js
: (#2006) by @frandioxexport default defineConfig({ plugins: [ ... ], + build: { + assetsInlineLimit: 0, + }, });
-
To improve HMR in Vite, move the
useRootLoaderData
function fromapp/root.tsx
to a separate file likeapp/lib/root-data.ts
. This change avoids circular imports: (#2014) by @frandiox// app/lib/root-data.ts import {useMatches} from '@remix-run/react'; import type {SerializeFrom} from '@shopify/remix-oxygen'; import type {loader} from '~/root'; /** * Access the result of the root loader from a React component. */ export const useRootLoaderData = () => { const [root] = useMatches(); return root?.data as SerializeFrom<typeof loader>; };
Import this hook from
~/lib/root-data
instead of~/root
in your components. -
Updated dependencies [
b4dfda32
,ffa57bdb
,ac4e1670
,0af624d5
,9723eaf3
,e842f68c
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Update internal libraries for parsing
.env
files. (#1946) by @aswamyPlease update the
@shopify/cli
dependency in your app to avoid duplicated subdependencies:"dependencies": { - "@shopify/cli": "3.56.3", + "@shopify/cli": "3.58.0", }
-
Add Adds magic Catalog route (#1967) by @juanpprieto
-
Update Vite plugin imports, and how their options are passed to Remix: (#1935) by @frandiox
-import {hydrogen, oxygen} from '@shopify/cli-hydrogen/experimental-vite'; +import {hydrogen} from '@shopify/hydrogen/vite'; +import {oxygen} from '@shopify/mini-oxygen/vite'; import {vitePlugin as remix} from '@remix-run/dev'; export default defineConfig({ hydrogen(), oxygen(), remix({ - buildDirectory: 'dist', + presets: [hydrogen.preset()], future: {
-
Add
@shopify/mini-oxygen
as a dev dependency for local development: (#1891) by @frandiox"devDependencies": { "@remix-run/dev": "^2.8.0", "@remix-run/eslint-config": "^2.8.0", + "@shopify/mini-oxygen": "^3.0.0", "@shopify/oxygen-workers-types": "^4.0.0", ... },
-
Add the
customer-account push
command to the Hydrogen CLI. This allows you to push the current--dev-origin
URL to the Shopify admin to enable secure connection to the Customer Account API for local development. (#1804) by @michenly -
Fix types returned by the
session
object. (#1869) by @frandioxIn
remix.env.d.ts
orenv.d.ts
, add the following types:import type { // ... HydrogenCart, + HydrogenSessionData, } from '@shopify/hydrogen'; // ... declare module '@shopify/remix-oxygen' { // ... + interface SessionData extends HydrogenSessionData {} }
-
Codegen dependencies must be now listed explicitly in
package.json
: (#1962) by @frandiox{ "devDependencies": { + "@graphql-codegen/cli": "5.0.2", "@remix-run/dev": "^2.8.0", "@remix-run/eslint-config": "^2.8.0", + "@shopify/hydrogen-codegen": "^0.3.0", "@shopify/mini-oxygen": "^2.2.5", "@shopify/oxygen-workers-types": "^4.0.0", ... } }
-
Updated dependencies [
4eaec272
,14bb5df1
,646b78d4
,87072950
,5f1295fe
,3c8a7313
,ca1dcbb7
,11879b17
,f4d6e5b0
,788d86b3
,ebaf5529
,da95bb1c
,5bb43304
,140e4768
,062d6be7
,b3323e59
,ab0df5a5
,ebaf5529
,ebaf5529
,9e899218
,a209019f
,d007b7bc
,a5511cd7
,4afedb4d
,34fbae23
,e3baaba5
,99d72f7a
,9351f9f5
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]
-
Improve performance of predictive search: (#1823) by @frandiox
- Change the request to be GET instead of POST to avoid Remix route revalidations.
- Add Cache-Control headers to the response to get quicker results when typing.
Aside from that, it now shows a loading state when fetching the results instead of "No results found.".
-
Updated dependencies [
351b3c1b
,5060cf57
,2888014e
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Update the
@shopify/cli
dependency: (#1786) by @frandiox- "@shopify/cli": "3.52.0", + "@shopify/cli": "3.56.3",
-
Update Remix and associated packages to 2.8.0. (#1781) by @frandiox
"dependencies": { - "@remix-run/react": "^2.6.0", - "@remix-run/server-runtime": "^2.6.0", + "@remix-run/react": "^2.8.0", + "@remix-run/server-runtime": "^2.8.0", //... }, "devDependencies": { - "@remix-run/dev": "^2.6.0", - "@remix-run/eslint-config": "^2.6.0", + "@remix-run/dev": "^2.8.0", + "@remix-run/eslint-config": "^2.8.0", //... },
-
Updated dependencies [
ced1d4cb
,fc013401
,e641255e
,d7e04cb6
,eedd9c49
]:- @shopify/[email protected]
- @shopify/[email protected]
-
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) by @blittleimport {type LoaderFunctionArgs} from '@remix-run/server-runtime'; export async function loader({params, context}: LoaderFunctionArgs) { const {language, country} = context.storefront.i18n; if ( params.locale && params.locale.toLowerCase() !== `${language}-${country}`.toLowerCase() ) { // If the locale URL param is defined, yet we still are still at the default locale // then the the locale param must be invalid, send to the 404 page throw new Response(null, {status: 404}); } return null; }
-
Add defensive null checks to the default cart implementation in the starter template (#1746) by @blittle
-
🐛 Fix issue where customer login does not persist to checkout (#1719) by @michenly
✨ Add
customerAccount
option tocreateCartHandler
. Where a?logged_in=true
will be added to the checkoutUrl for cart query if a customer is logged in. -
Updated dependencies [
faeba9f8
,6d585026
,fcecfb23
,28864d6f
,c0ec7714
,226cf478
,06d9fd91
]:- @shopify/[email protected]
- @shopify/[email protected]
-
♻️
CustomerClient
type is deprecated and replaced byCustomerAccount
(#1692) by @michenly -
Updated dependencies [
02798786
,52b15df4
,a2664362
,eee5d927
,c7b2017f
,06320ee4
]:- @shopify/[email protected]
- @shopify/[email protected]
-
Use new parameters introduced in Storefront API v2024-01 to fix redirection to the product's default variant when there are unknown query params in the URL. (#1642) by @wizardlyhel
- selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) { + selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions, ignoreUnknownOptions: true, caseInsensitiveMatch: true) { ...ProductVariant }
-
Update the GraphQL config in
.graphqlrc.yml
to use the more modernprojects
structure: (#1577) by @frandiox-schema: node_modules/@shopify/hydrogen/storefront.schema.json +projects: + default: + schema: 'node_modules/@shopify/hydrogen/storefront.schema.json'
This allows you to add additional projects to the GraphQL config, such as third party CMS schemas.
Also, you can modify the document paths used for the Storefront API queries. This is useful if you have a large codebase and want to exclude certain files from being used for codegen or other GraphQL utilities:
projects: default: schema: 'node_modules/@shopify/hydrogen/storefront.schema.json' documents: - '!*.d.ts' - '*.{ts,tsx,js,jsx}' - 'app/**/*.{ts,tsx,js,jsx}'
-
Update
@shopify/cli
dependency inpackage.json
: (#1579) by @frandiox- "@shopify/cli": "3.51.0", + "@shopify/cli": "3.52.0",
-
-
Update example and template Remix versions to
^2.5.1
(#1639) by @wizardlyhel -
Enable Remix future flags:
-
-
Updated dependencies [
810f48cf
,8c477cb5
,42ac4138
,0241b7d2
,6a897586
,0ff63bed
,6bc1d61c
,eb0f4bcc
,400bfee6
,a69c21ca
,970073e7
,772118ca
,335375a6
,335371ce
,94509b75
,36d6fa2c
,3e7b6e8a
,cce65795
,9e3d88d4
,ca1161b2
,92840e51
,952fedf2
,1bc053c9
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]
-
Sync up environment variable names across all example & type files. (#1542) by @michenly
-
Remove error boundary from robots.txt file in the Skeleton template (#1492) by @andrewcohen
-
Use the worker runtime by default when running the
dev
orpreview
commands. (#1525) by @frandioxEnable it in your project by adding the
--worker
flag to your package.json scripts:"scripts": { "build": "shopify hydrogen build", - "dev": "shopify hydrogen dev --codegen", + "dev": "shopify hydrogen dev --worker --codegen", - "preview": "npm run build && shopify hydrogen preview", + "preview": "npm run build && shopify hydrogen preview --worker", ... }
-
Update to the latest version of
@shopify/oxygen-workers-types
. (#1494) by @frandioxIn TypeScript projects, when updating to the latest
@shopify/remix-oxygen
adapter release, you should also update to the latest version of@shopify/oxygen-workers-types
:"devDependencies": { "@remix-run/dev": "2.1.0", "@remix-run/eslint-config": "2.1.0", - "@shopify/oxygen-workers-types": "^3.17.3", + "@shopify/oxygen-workers-types": "^4.0.0", "@shopify/prettier-config": "^1.1.2", ... },
-
Update internal dependencies for bug resolution. (#1496) by @vincentezw
Update your
@shopify/cli
dependency to avoid duplicated sub-dependencies:"dependencies": { - "@shopify/cli": "3.50.2", + "@shopify/cli": "3.51.0", }
-
Update all Node.js dependencies to version 18. (Not a breaking change, since Node.js 18 is already required by Remix v2.) (#1543) by @michenly
-
Add
@remix-run/server-runtime
dependency. (#1489) by @frandioxSince Remix is now a peer dependency of
@shopify/remix-oxygen
, you need to add@remix-run/server-runtime
to your dependencies, with the same version as the rest of your Remix dependencies."dependencies": { "@remix-run/react": "2.1.0" + "@remix-run/server-runtime": "2.1.0" ... }
-
Updated dependencies [
b2a350a7
,9b4f4534
,74ea1dba
,2be9ce82
,a9b8bcde
,bca112ed
,848c6260
,d53b4ed7
,961fd8c6
,2bff9fc7
,c8e8f6fd
,8fce70de
,f90e4d47
,e8cc49fe
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]
-
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 theprimaryDomainUrl
: by @blittle- Update the
HeaderMenu
component to accept aprimaryDomainUrl
and include it in the internal url check
// app/components/Header.tsx + import type {HeaderQuery} from 'storefrontapi.generated'; export function HeaderMenu({ menu, + primaryDomainUrl, viewport, }: { menu: HeaderProps['header']['menu']; + primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url']; viewport: Viewport; }) { // ...code // if the url is internal, we strip the domain const url = item.url.includes('myshopify.com') || item.url.includes(publicStoreDomain) || + item.url.includes(primaryDomainUrl) ? new URL(item.url).pathname : item.url; // ...code }
- Update the
FooterMenu
component to accept aprimaryDomainUrl
prop and include it in the internal url check
// app/components/Footer.tsx - import type {FooterQuery} from 'storefrontapi.generated'; + import type {FooterQuery, HeaderQuery} from 'storefrontapi.generated'; function FooterMenu({ menu, + primaryDomainUrl, }: { menu: FooterQuery['menu']; + primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url']; }) { // code... // if the url is internal, we strip the domain const url = item.url.includes('myshopify.com') || item.url.includes(publicStoreDomain) || + item.url.includes(primaryDomainUrl) ? new URL(item.url).pathname : item.url; // ...code ); }
- Update the
Footer
component to accept ashop
prop
export function Footer({ menu, + shop, }: FooterQuery & {shop: HeaderQuery['shop']}) { return ( <footer className="footer"> - <FooterMenu menu={menu} /> + <FooterMenu menu={menu} primaryDomainUrl={shop.primaryDomain.url} /> </footer> ); }
- Update
Layout.tsx
to pass theshop
prop
export function Layout({ cart, children = null, footer, header, isLoggedIn, }: LayoutProps) { return ( <> <CartAside cart={cart} /> <SearchAside /> <MobileMenuAside menu={header.menu} shop={header.shop} /> <Header header={header} cart={cart} isLoggedIn={isLoggedIn} /> <main>{children}</main> <Suspense> <Await resolve={footer}> - {(footer) => <Footer menu={footer.menu} />} + {(footer) => <Footer menu={footer.menu} shop={header.shop} />} </Await> </Suspense> </> ); }
- Update the
-
If you are calling
useMatches()
in different places of your app to access the data returned by the root loader, you may want to update it to the following pattern to enhance types: (#1289) by @frandiox// root.tsx import {useMatches} from '@remix-run/react'; import {type SerializeFrom} from '@shopify/remix-oxygen'; export const useRootLoaderData = () => { const [root] = useMatches(); return root?.data as SerializeFrom<typeof loader>; }; export function loader(context) { // ... }
This way, you can import
useRootLoaderData()
anywhere in your app and get the correct type for the data returned by the root loader. -
Updated dependencies [
81400439
,a6f397b6
,3464ec04
,7fc088e2
,867e0b03
,ad45656c
,f24e3424
,66a48573
,0ae7cbe2
,8198c1be
,ad45656c
]:- @shopify/[email protected]
- @shopify/[email protected]
- @shopify/[email protected]