Skip to main content

Update cart metafields

This guide shows you how to use a cart handler to update cart metafields.



Anchor to Step 1: Read cart metafieldsStep 1: Read cart metafields

Update the cart query fragment to return cart metafields. For more information, refer to the default CartApiQuery fragment.

The following example adds themetafield field:

File

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
}
`;
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
}
}
`;

Anchor to Step 2: Create a metafield formStep 2: Create a metafield form

Use React Router's useFetcher 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.

File

/app/components/ThisIsGift.jsx

import {useFetcher} from '@react-router';
import {CartForm} from '@shopify/hydrogen';

export function ThisIsGift({metafield}) {
const fetcher = useFetcher();

return (
<div>
<input
checked={metafield?.value === 'true'}
type="checkbox"
id="isGift"
onChange={(event) = /> {
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'}
)
}}
/>
<label htmlFor="isGift">This is a gift</label>
</div>
);
}
import {useFetcher} from '@react-router';
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 (
<div>
<input
checked={metafield?.value === 'true'}
type="checkbox"
id="isGift"
onChange={(event) = /> {
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'}
)
}}
/>
<label htmlFor="isGift">This is a gift</label>
</div>
);
}

Anchor to Step 3: Handle the update metafield form requestStep 3: Handle the update metafield form request

Handle the update metafield form request in an action. Use the cart, created from createCartHandler, to handle cart mutation requests to the Storefront API.

File

/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},
);
}
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},
);
}


Was this page helpful?