Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions cookbook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,13 @@ cookbook.ts validate
Validate a recipe

Options:
--version Show version number [boolean]
--help Show help [boolean]
--recipe The name of the recipe to validate. If not provided,
all recipes will be validated. [string]
--version Show version number [boolean]
--help Show help [boolean]
--recipe The name of the recipe to validate. If not
provided, all recipes will be validated. [string]
--hydrogenPackagesVersion The version of Hydrogen to use for the recipe. If
not provided, the latest version will be used.
[string]
```

#### Example
Expand Down
55 changes: 41 additions & 14 deletions cookbook/llms/bundles.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ In this recipe, we'll use the [Shopify Bundles app](https://apps.shopify.com/sho

Create a new BundleBadge component to be displayed on bundle product listings.

#### File: [BundleBadge.tsx](https://github.com/Shopify/hydrogen/blob/87da752246ad519f744a791cd21fd75546c7273e/cookbook/recipes/bundles/ingredients/templates/skeleton/app/components/BundleBadge.tsx)
#### File: [BundleBadge.tsx](https://github.com/Shopify/hydrogen/blob/a37399a6a6a668f40194f40a9f3dc988244df303/cookbook/recipes/bundles/ingredients/templates/skeleton/app/components/BundleBadge.tsx)

```tsx
export function BundleBadge() {
Expand All @@ -108,10 +108,10 @@ export function BundleBadge() {

Create a new `BundledVariants` component that wraps the variants of a bundle product in a single product listing.

#### File: [BundledVariants.tsx](https://github.com/Shopify/hydrogen/blob/87da752246ad519f744a791cd21fd75546c7273e/cookbook/recipes/bundles/ingredients/templates/skeleton/app/components/BundledVariants.tsx)
#### File: [BundledVariants.tsx](https://github.com/Shopify/hydrogen/blob/a37399a6a6a668f40194f40a9f3dc988244df303/cookbook/recipes/bundles/ingredients/templates/skeleton/app/components/BundledVariants.tsx)

```tsx
import {Link} from '@remix-run/react';
import {Link} from 'react-router';
import {Image} from '@shopify/hydrogen';
import type {
ProductVariantComponent,
Expand Down Expand Up @@ -177,7 +177,34 @@ export function BundledVariants({

```

### Step 4: Update the product fragment to query for bundles and display BundledVariants
### Step 4: Add maxVariantPrice to the RecommendedProducts query's product fields

Add `maxVariantPrice` to the `RecommendedProducts` query's product fields.

#### File: /app/routes/_index.tsx

```diff
@@ -1,5 +1,5 @@
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
-import { Await, useLoaderData, Link, type MetaFunction } from 'react-router';
+import {Await, useLoaderData, Link, type MetaFunction} from 'react-router';
import {Suspense} from 'react';
import {Image, Money} from '@shopify/hydrogen';
import type {
@@ -147,6 +147,10 @@ const RECOMMENDED_PRODUCTS_QUERY = `#graphql
amount
currencyCode
}
+ maxVariantPrice {
+ amount
+ currencyCode
+ }
}
featuredImage {
id
```

### Step 5: Update the product fragment to query for bundles and display BundledVariants

1. Add the `requiresComponents` field to the `Product` fragment, which is
used to identify bundled products.
Expand All @@ -189,7 +216,7 @@ used to identify bundled products.
@@ -1,4 +1,4 @@
-import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
+import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, type MetaFunction} from '@remix-run/react';
import { useLoaderData, type MetaFunction } from 'react-router';
import {
getSelectedProductOptions,
@@ -12,6 +12,8 @@ import {ProductPrice} from '~/components/ProductPrice';
Expand Down Expand Up @@ -295,7 +322,7 @@ used to identify bundled products.
title
```

### Step 5: Update the collections fragment to query for bundles
### Step 6: Update the collections fragment to query for bundles

Like the previous step, use the `requiresComponents` field to detect if the product item is a bundle.

Expand Down Expand Up @@ -345,7 +372,7 @@ Like the previous step, use the `requiresComponents` field to detect if the prod
query Collection(
```

### Step 6: Update the cart fragment to query for bundles
### Step 7: Update the cart fragment to query for bundles

Use the `requiresComponents` field to determine if a cart line item is a bundle.

Expand Down Expand Up @@ -403,14 +430,14 @@ Use the `requiresComponents` field to determine if a cart line item is a bundle.
}
```

### Step 7: Conditionally render the BundleBadge in cart line items
### Step 8: Conditionally render the BundleBadge in cart line items

If a product is a bundle, show the `BundleBadge` component in the cart line item.

#### File: /app/components/CartLineItem.tsx

```diff
@@ -6,6 +6,7 @@ import {Link} from '@remix-run/react';
@@ -6,6 +6,7 @@ import { Link } from 'react-router';
import {ProductPrice} from './ProductPrice';
import {useAside} from './Aside';
import type {CartApiQueryFragment} from 'storefrontapi.generated';
Expand Down Expand Up @@ -451,7 +478,7 @@ If a product is a bundle, show the `BundleBadge` component in the cart line item
<ul>
```

### Step 8: Conditionally render "Add bundle to cart" in ProductForm
### Step 9: Conditionally render "Add bundle to cart" in ProductForm

If a product is a bundle, update the text of the product button.

Expand Down Expand Up @@ -485,7 +512,7 @@ If a product is a bundle, update the text of the product button.
);
```

### Step 9: Conditionally render the BundleBadge in ProductImage
### Step 10: Conditionally render the BundleBadge in ProductImage

If a product is a bundle, show the `BundleBadge` component in the `ProductImage` component.

Expand Down Expand Up @@ -516,15 +543,15 @@ If a product is a bundle, show the `BundleBadge` component in the `ProductImage`
}
```

### Step 10: Conditionally render the BundleBadge in ProductItem
### Step 11: Conditionally render the BundleBadge in ProductItem

If a product is a bundle, show the `BundleBadge` component in the `ProductItem` component.

#### File: /app/components/ProductItem.tsx

```diff
@@ -1,24 +1,19 @@
import {Link} from '@remix-run/react';
import {Link} from 'react-router';
import {Image, Money} from '@shopify/hydrogen';
-import type {
- ProductItemFragment,
Expand Down Expand Up @@ -591,7 +618,7 @@ If a product is a bundle, show the `BundleBadge` component in the `ProductItem`
}
```

### Step 11: Add a product-image class to the app stylesheet
### Step 12: Add a product-image class to the app stylesheet

Make sure the bundle badge is positioned relative to the product image.

Expand Down
10 changes: 5 additions & 5 deletions cookbook/llms/combined-listings.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const combinedListingsSettings = {

Create a new `combined-listings.ts` file that contains utilities and settings for handling combined listings.

#### File: [combined-listings.ts](https://github.com/Shopify/hydrogen/blob/87da752246ad519f744a791cd21fd75546c7273e/cookbook/recipes/combined-listings/ingredients/templates/skeleton/app/lib/combined-listings.ts)
#### File: [combined-listings.ts](https://github.com/Shopify/hydrogen/blob/2e32e77efa32aca00b18552fbdbfcb8af012f4ca/cookbook/recipes/combined-listings/ingredients/templates/skeleton/app/lib/combined-listings.ts)

```ts
// Edit these values to customize combined listings' behavior
Expand Down Expand Up @@ -345,7 +345,7 @@ If you want to redirect automatically to the first variant of a combined listing
```diff
@@ -1,13 +1,13 @@
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {Await, useLoaderData, Link, type MetaFunction} from '@remix-run/react';
import { Await, useLoaderData, Link, type MetaFunction } from 'react-router';
import {Suspense} from 'react';
-import {Image, Money} from '@shopify/hydrogen';
+import {Image} from '@shopify/hydrogen';
Expand Down Expand Up @@ -486,8 +486,8 @@ Update the `collections.all` route to filter out combined listings from the sear
#### File: /app/routes/collections.all.tsx

```diff
@@ -3,7 +3,10 @@ import {useLoaderData, type MetaFunction} from '@remix-run/react';
import {getPaginationVariables} from '@shopify/hydrogen';
@@ -3,7 +3,10 @@ import {useLoaderData, type MetaFunction} from 'react-router';
import {getPaginationVariables, Image, Money} from '@shopify/hydrogen';
import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
import {ProductItem} from '~/components/ProductItem';
-
Expand Down Expand Up @@ -545,7 +545,7 @@ Update the `collections.all` route to filter out combined listings from the sear
@@ -1,4 +1,4 @@
-import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
+import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, type MetaFunction} from '@remix-run/react';
import { useLoaderData, type MetaFunction } from 'react-router';
import {
getSelectedProductOptions,
@@ -11,7 +11,14 @@ import {
Expand Down
92 changes: 71 additions & 21 deletions cookbook/llms/markets.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ Create a new `CountrySelector` component that allows users to select the locale
To handle redirects, use a `Form` that updates the cart buyer identity,
which eventually redirects to the localized root of the app.

##### File: [CountrySelector.tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/components/CountrySelector.tsx)
##### File: [CountrySelector.tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/components/CountrySelector.tsx)

```tsx
import {Form} from '@remix-run/react';
import {Form} from 'react-router';
import {Locale, SUPPORTED_LOCALES, useSelectedLocale} from '../lib/i18n';
import {CartForm} from '@shopify/hydrogen';

Expand Down Expand Up @@ -189,22 +189,21 @@ const LocaleLink = ({locale}: {locale: Locale}) => {

#### Step 1.2: Create a Link wrapper component

Create a wrapper component around the Remix `Link` component that prepends the selected locale path prefix (if any) to the actual links.
Create a wrapper component around the `Link` component that prepends the selected locale path prefix (if any) to the actual links.

##### File: [Link.tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/components/Link.tsx)
##### File: [Link.tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/components/Link.tsx)

```tsx
import {Link as RemixLink} from '@remix-run/react';
import {RemixLinkProps} from '@remix-run/react/dist/components';
import {LinkProps, Link as ReactLink} from 'react-router';
import {useSelectedLocale} from '../lib/i18n';

export function Link({...props}: RemixLinkProps) {
export function Link({...props}: LinkProps) {
const selectedLocale = useSelectedLocale();

const prefix = selectedLocale?.pathPrefix.replace(/\/+$/, '') ?? '';
const to = `${prefix}${props.to}`;

return <RemixLink {...props} to={to} />;
return <ReactLink {...props} to={to} />;
}

```
Expand All @@ -216,10 +215,10 @@ a hook to retrieve the selected locale.
2. Define a set of supported locales for the app.
3. Add a utility function to validate the locale from the route param against the supported locales.

##### File: [i18n.ts](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/lib/i18n.ts)
##### File: [i18n.ts](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/lib/i18n.ts)

```ts
import {useMatches} from '@remix-run/react';
import {useMatches} from 'react-router';
import {
LanguageCode,
CountryCode,
Expand Down Expand Up @@ -305,7 +304,7 @@ Update the `ProductItem` component to use the `Link` component from the

```diff
@@ -1,4 +1,3 @@
-import {Link} from '@remix-run/react';
-import {Link} from 'react-router';
import {Image, Money} from '@shopify/hydrogen';
import type {
ProductItemFragment,
Expand Down Expand Up @@ -414,7 +413,7 @@ when the locale changes.

### Step 2: Localizing the individual routes

In this section, we'll add localization to the individual routes using the language [dynamic segment](https://remix.run/docs/en/main/file-conventions/routes#optional-segments).
In this section, we'll add localization to the individual routes using the language [dynamic segment](https://reactrouter.com/start/data/routing#optional-segments).

#### Step 2.1: Add language dynamic segment to the desired routes

Expand All @@ -427,17 +426,16 @@ For brevity, we'll focus on the home page, the cart page, and the product page i
#### Step 2.2: Add localization to the home page

1. Add the dynamic segment to the home page route.
2. Use the new `Link` component as a drop-in replacement for the existing
Remix counterpart.
2. Use the new `Link` component as a drop-in replacement.

> [!NOTE]
> Rename `app/routes/_index.tsx` to `app/routes/($locale)._index.tsx`.

##### File: [($locale)._index.tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale)._index.tsx)
##### File: [($locale)._index.tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale)._index.tsx)

```tsx
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {Await, useLoaderData, type MetaFunction} from '@remix-run/react';
import {Await, useLoaderData, type MetaFunction} from 'react-router';
import {Suspense} from 'react';
import {Image, Money} from '@shopify/hydrogen';
import type {
Expand Down Expand Up @@ -614,10 +612,10 @@ Add the dynamic segment to the cart page route.
> [!NOTE]
> Rename `app/routes/cart.tsx` to `app/routes/($locale).cart.tsx`.

##### File: [($locale).cart.tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).cart.tsx)
##### File: [($locale).cart.tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).cart.tsx)

```tsx
import {type MetaFunction, useLoaderData} from '@remix-run/react';
import {type MetaFunction, useLoaderData} from 'react-router';
import type {CartQueryDataReturn} from '@shopify/hydrogen';
import {CartForm} from '@shopify/hydrogen';
import {
Expand Down Expand Up @@ -746,11 +744,11 @@ localized prefix.
> [!NOTE]
> Rename `app/routes/products.$handle.tsx` to `app/routes/($locale).products.$handle.tsx`.

##### File: [($locale).products.$handle.tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).products.$handle.tsx)
##### File: [($locale).products.$handle.tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).products.$handle.tsx)

```tsx
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, type MetaFunction} from '@remix-run/react';
import {useLoaderData, type MetaFunction} from 'react-router';
import {
getSelectedProductOptions,
Analytics,
Expand Down Expand Up @@ -1009,7 +1007,7 @@ Add a utility route in `$(locale).tsx` that will use `localeMatchesPrefix`
to validate the locale from the URL params. If the locale is invalid,
the route will throw a 404 error.

##### File: [($locale).tsx](https://github.com/Shopify/hydrogen/blob/a7e33c1dd45e3c7c27ab2e1125851468051cee0b/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).tsx)
##### File: [($locale).tsx](https://github.com/Shopify/hydrogen/blob/d63f8518186b4487a445a4ed79139ba4d8b15550/cookbook/recipes/markets/ingredients/templates/skeleton/app/routes/($locale).tsx)

```tsx
import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
Expand Down Expand Up @@ -1052,6 +1050,58 @@ Update the sitemap route to use the locales included in `SUPPORTED_LOCALES`.
return `${baseUrl}/${locale}/${type}/${handle}`;
```

#### Step 2.7: Update the useVariantUrl function.

Remove the `pathname` parameter from the `useVariantUrl` function, and the logic that prepends the locale to the path.

##### File: /app/lib/variants.ts

```diff
@@ -1,4 +1,3 @@
-import { useLocation } from 'react-router';
import type {SelectedOption} from '@shopify/hydrogen/storefront-api-types';
import {useMemo} from 'react';

@@ -6,35 +5,25 @@ export function useVariantUrl(
handle: string,
selectedOptions?: SelectedOption[],
) {
- const {pathname} = useLocation();
-
return useMemo(() => {
return getVariantUrl({
handle,
- pathname,
searchParams: new URLSearchParams(),
selectedOptions,
});
- }, [handle, selectedOptions, pathname]);
+ }, [handle, selectedOptions]);
}

export function getVariantUrl({
handle,
- pathname,
searchParams,
selectedOptions,
}: {
handle: string;
- pathname: string;
searchParams: URLSearchParams;
selectedOptions?: SelectedOption[];
}) {
- const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
- const isLocalePathname = match && match.length > 0;
-
- const path = isLocalePathname
- ? `${match![0]}products/${handle}`
- : `/products/${handle}`;
+ const path = `/products/${handle}`;

selectedOptions?.forEach((option) => {
searchParams.set(option.name, option.value);
```

## Deleted Files

- [`templates/skeleton/app/routes/_index.tsx`](templates/skeleton/app/routes/_index.tsx)
Expand Down
Loading
Loading