Skip to main content

Product components

The createProductComponents function creates a product provider and product hooks for React or Vue, typed to your product query. Use the provider and hooks to share one product form across variant pickers and an add-to-cart form, and to update the URL when the customer selects an option.

To manage the store yourself, create the store with createProductFormStore and read the store with the standalone useProductForm hook. Other frameworks use createProductFormStore directly.

Import createProductComponents and useProductForm from @shopify/hydrogen/react or @shopify/hydrogen/vue.

Render ProductProvider inside the CartProvider from createCartComponents.


Anchor to createProductComponents
createProductComponents()

Creates a product provider and product hooks typed to your product query.

The provider creates the product form store, reloads the product into the store when the product changes, and destroys the store on unmount. The useProduct hook returns the option state and variant selection. The useProductForm hook adds field props, form props, and the pending state.

Pass your Storefront API product query type as the type argument to type the selected variant and option values. Render the provider inside a cart provider. The provider throws an error without a cart provider ancestor, and each hook throws an error outside the product provider.

{ ProductProvider: (props: <TProduct>) => ReactNode; useProduct: () => <TProduct>; useProductForm: () => <TProduct>; }

The typed ProductProvider component and the useProduct and useProductForm hooks.

Examples

React

import { createProductComponents } from "@shopify/hydrogen/react";

import type { ProductData } from "./types";

export const { ProductProvider, useProduct, useProductForm } =
createProductComponents<ProductData>();

Anchor to Standalone form hookStandalone form hook

Returns product form state and bindings from a product form store that you manage, and re-renders when the state changes.

Create the store with createProductFormStore. Call connect() on mount, hydrate() when the product data changes, and destroy() on unmount. To skip the store setup, use the product provider from createProductComponents.

Anchor to store
store
<<>, >
required

The store that createProductFormStore returns.

Anchor to options
options
<<>>

An onSelect callback that runs after each resolved or unresolved selection.

<>

The option state, the selected variant, cart errors, the matching cart line, the pending state, the register function, and the form props.

Examples

React

import {
canAddToCart,
createProductFormStore,
type CartStore,
type ProductInput,
} from "@shopify/hydrogen";
import { useProductForm } from "@shopify/hydrogen/react";
import { useEffect, useRef, useState } from "react";

export function AddToCart({ product, cartStore }: { product: ProductInput; cartStore: CartStore }) {
const [store] = useState(() => createProductFormStore(product, cartStore));

useEffect(() => {
store.connect();
return () => store.destroy();
}, [store]);

// Rehydrate when client-side navigation passes a different product.
const productKey = `${product.id}:${product.selectedOrFirstAvailableVariant?.id ?? ""}`;
const mountedRef = useRef(false);
useEffect(() => {
if (!mountedRef.current) {
mountedRef.current = true;
return;
}
store.hydrate(product);
}, [productKey]);

const { options, register, formProps, pending } = useProductForm(store);

return (
<form {...formProps()}>
<input type="hidden" {...register("merchandiseId", {})} />
<input type="hidden" {...register("quantity", { value: 1 })} />
<button {...register("addToCart", {})} disabled={!canAddToCart(product, options) || pending}>
Add to cart
</button>
</form>
);
}

  • The Vue ProductProvider takes product and onSelect props and renders its default slot.
  • The Vue useProduct and useProductForm composables return objects with getters. Read properties from the returned object. Destructured values don't update when the state changes.
  • The Vue pending value is a boolean shallow ref. Read pending.value in templates and scripts.
  • The composables from createProductComponents call the latest onSelect prop. The standalone useProductForm composable keeps the onSelect option from setup and ignores later changes.

Was this page helpful?