This guide shows you how to use a cart handler to update cart metafields. ## Requirements - You've completed the [quickstart guide](/docs/storefronts/headless/hydrogen/getting-started). - You've [set up a cart handler](/docs/storefronts/headless/hydrogen/cart/setup). ## Step 1: Read cart metafields Update the cart query fragment to return cart metafields. For more information, refer to the [default `CartApiQuery` fragment](https://github.com/Shopify/hydrogen/blob/main/packages/hydrogen/src/cart/queries/cartGetDefault.ts). The following example adds the`metafield` field: ```jsx?title: 'JavaScript', filename: 'server.js' const cart = createCartHandler({ storefront, getCartId: cartGetIdDefault(request.headers), setCartId: cartSetIdDefault(), cartQueryFragment: CART_QUERY_FRAGMENT, }); const CART_QUERY_FRAGMENT = `#graphql fragment CartApiQuery on Cart { metafield(namespace: "custom", key: "gift") { value } id checkoutUrl totalQuantity buyerIdentity { countryCode customer { id email firstName lastName displayName } email phone } lines(first: $numCartLines) { edges { node { id quantity attributes { key value } cost { totalAmount { amount currencyCode } amountPerQuantity { amount currencyCode } compareAtAmountPerQuantity { amount currencyCode } } merchandise { ... on ProductVariant { id availableForSale compareAtPrice { ...CartApiMoney } price { ...CartApiMoney } requiresShipping title image { ...CartApiImage } product { handle title id } selectedOptions { name value } } } } } } cost { subtotalAmount { ...CartApiMoney } totalAmount { ...CartApiMoney } totalDutyAmount { ...CartApiMoney } totalTaxAmount { ...CartApiMoney } } note attributes { key value } discountCodes { applicable code } } fragment CartApiMoney on MoneyV2 { currencyCode amount } fragment CartApiImage on Image { id url altText width height } `; ``` ```jsx?title: 'TypeScript', filename: 'server.ts' const cart = createCartHandler({ storefront, getCartId: cartGetIdDefault(request.headers), setCartId: cartSetIdDefault(), cartQueryFragment: CART_QUERY_FRAGMENT, }); const CART_QUERY_FRAGMENT = `#graphql fragment CartApiQuery on Cart { id metafield(namespace: "custom", key: "gift") { value } } `; ``` ## Step 2: Create a metafield form Use Remix's [`useFetcher`](https://remix.run/docs/en/main/hooks/use-fetcher) hook to create a form that submits information that you want to store in a metafield. The hook submits a form request to the `/cart` route's action when users submit with this metafield form. You can use this component anywhere in the app. When you use `fetcher.submit`, make sure there's a data key with the name `CartForm.INPUT_NAME`. The key value must be a JSON stringified object with `action` and `inputs` defined. ```jsx?title: 'JavaScript', filename: '/app/components/ThisIsGift.jsx' import {useFetcher} from '@remix-run/react'; import {CartForm} from '@shopify/hydrogen'; export function ThisIsGift({metafield}) { const fetcher = useFetcher(); return (
{ fetcher.submit( { [CartForm.INPUT_NAME]: JSON.stringify({ action: CartForm.ACTIONS.MetafieldsSet, inputs: { metafields: [{ key: 'custom.gift', type: 'boolean', value: event.target.checked.toString(), }], }, }), }, {method: 'POST', action: '/cart'} ) }} />
); } ``` ```jsx?title: 'TypeScript', filename: '/app/components/ThisIsGift.tsx' import {useFetcher} from '@remix-run/react'; import {CartForm} from '@shopify/hydrogen'; import type {Cart} from '@shopify/hydrogen/storefront-api-types'; export function ThisIsGift({ metafield, }: { metafield: Cart['metafield']; }) { const fetcher = useFetcher(); return (
{ fetcher.submit( { [CartForm.INPUT_NAME]: JSON.stringify({ action: CartForm.ACTIONS.MetafieldsSet, inputs: { metafields: [{ key: 'custom.gift', type: 'boolean', value: event.target.checked.toString(), }], }, }), }, {method: 'POST', action: '/cart'} ) }} />
); } ``` ## Step 3: Handle the update metafield form request Handle the update metafield form request in an `action`. Use the `cart`, created from [`createCartHandler`](/docs/api/hydrogen/latest/utilities/createcarthandler), to handle cart mutation requests to the Storefront API. ```jsx?title: 'JavaScript', filename: '/app/routes/cart.jsx' import {CartForm} from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; export async function action({request, context}) { const {cart} = context; const formData = await request.formData(); const {action, inputs} = CartForm.getFormInput(formData); let result; switch(action) { case CartForm.ACTIONS.MetafieldsSet: result = await cart.setMetafields(inputs.metafields); break; default: invariant(false, `${action} cart action is not defined`); } // The Cart ID might change after each mutation, so update it each time. const headers = cart.setCartId(result.cart.id); return json( result, {status: 200, headers}, ); } ``` ```jsx?title: 'TypeScript', filename: '/app/routes/cart.tsx' import { type CartQueryData, CartForm, } from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; export async function action({request, context}: ActionArgs) { const {cart} = context; const formData = await request.formData(); const {action, inputs} = CartForm.getFormInput(formData); let result: CartQueryData; switch(action) { case CartForm.ACTIONS.MetafieldsSet: result = await cart.setMetafields(inputs.metafields); break; default: invariant(false, `${action} cart action is not defined`); } // The Cart ID might change after each mutation, so update it each time. const headers = cart.setCartId(result.cart.id); return json( result, {status: 200, headers}, ); } ``` ## Next steps - Learn how to [update buyer identity](/docs/storefronts/headless/hydrogen/cart/buyer-identity).