--- title: Attributes API description: >- Use this API to read and modify custom key-value pairs attached to the cart and checkout. You can store buyer preferences like gift wrapping and loyalty data. api_version: 2026-04 api_name: checkout-ui-extensions source_url: html: >- https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis/checkout-apis/attributes-api md: >- https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis/checkout-apis/attributes-api.md --- # Attributes API Use the Attributes API to read and write custom key-value pairs attached to the cart that persist through checkout. These key-value pairs are a lightweight way to keep buyer choices, operational details, and feature flags on the cart so that extensions and merchant workflows can all access the same values. Attributes are shared across all extensions and the storefront. Any extension can read or overwrite attributes set by another. **Use metafields instead of attributes:** To store custom data, Shopify recommends that you use the [Metafields API](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/metafields) to write cart [metafields](https://shopify.dev/docs/apps/build/checkout/metafields). Metafields are safer than cart attributes as they support edit and view permissions and app-reserved namespaces to prevent other apps from reading and writing data. ### Use cases * **Track buyer preferences**: Store buyers' custom selections like gift wrapping or personalization options as key-value pairs on the cart. * **Conditionally display UI**: Show or hide features based on the cart's current attributes. * **Store operational data**: Capture purchase order numbers, reference codes, or other operational data that the merchant needs for accounting or shipping. ### Support Targets (33) ### Supported targets * [purchase.​address-autocomplete.​format-suggestion](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/address#format-a-selected-suggestion-) * [purchase.​address-autocomplete.​suggest](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/address#suggest-address-completions-) * [purchase.​checkout.​actions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/navigation#navigation-target) * [purchase.​checkout.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/block#block-target) * [purchase.​checkout.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#line-item-targets) * [purchase.​checkout.​cart-line-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#checkout-cart-line-list-) * purchase.​checkout.​chat.​render * [purchase.​checkout.​contact.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/information#information-target) * [purchase.​checkout.​delivery-address.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-delivery-address-) * [purchase.​checkout.​delivery-address.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#delivery-address-targets) * [purchase.​checkout.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/footer#footer-target) * [purchase.​checkout.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/header#header-target) * [purchase.​checkout.​payment-method-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/payment#render-after-payment-methods-) * [purchase.​checkout.​payment-method-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/payment#payment-targets) * [purchase.​checkout.​pickup-location-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#render-after-pickup-locations-) * [purchase.​checkout.​pickup-location-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#location-list-targets) * [purchase.​checkout.​pickup-location-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#location-option-item-target) * [purchase.​checkout.​pickup-point-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/pickup-points#render-after-pickup-points-) * [purchase.​checkout.​pickup-point-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/pickup-points#pickup-points-targets) * [purchase.​checkout.​reductions.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#checkout-reductions-after-) * [purchase.​checkout.​reductions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#reductions-targets) * [purchase.​checkout.​shipping-option-item.​details.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#shipping-option-item-targets) * [purchase.​checkout.​shipping-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-shipping-option-) * [purchase.​checkout.​shipping-option-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-shipping-options-) * [purchase.​checkout.​shipping-option-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#shipping-option-list-targets) * [purchase.​thank-you.​announcement.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/announcement#thank-you-announcement-) * [purchase.​thank-you.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/block#block-target) * [purchase.​thank-you.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/order-summary#line-item-targets) * [purchase.​thank-you.​cart-line-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/order-summary#thank-you-cart-line-list-) * purchase.​thank-you.​chat.​render * [purchase.​thank-you.​customer-information.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/information#information-target) * [purchase.​thank-you.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/footer#footer-target) * [purchase.​thank-you.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/header#header-target) ### Properties The [`shopify` global object](https://shopify.dev/docs/api/checkout-ui-extensions/latest#target-apis-define-what-your-extension-does) provides attribute data for the current checkout. Access the following properties on `shopify` to read custom key-value pairs attached to the cart. Available to `purchase` extension targets. * **attributes** **SubscribableSignalLike\** **required** The custom key-value attributes attached to the cart or checkout. These are set by the buyer or by an extension using `applyAttributeChange()`. The list is empty if no attributes have been added. ### SubscribableSignalLike Represents a reactive signal interface that provides both immediate value access and subscription-based updates. Enables real-time synchronization with changing data through the observer pattern. This interface extends \`ReadonlySignalLike\` with deprecated fields that are still supported for backwards compatibility. * current The current value of the signal. Equivalent to \`.value\`, accessing this property subscribes to changes when used in a reactive context. ```ts T ``` * destroy Cleans up the subscription and releases any resources held by this signal. After calling \`destroy()\`, the signal stops receiving updates from the main thread. ```ts () => Promise ``` * subscribe Subscribes to value changes and calls the provided function whenever the value updates. Returns an unsubscribe function to clean up the subscription. Use to automatically react to changes in the signal's value. ```ts (fn: (value: T) => void) => () => void ``` * value The current value of the signal. This property provides immediate access to the current value without requiring subscription setup. Use for one-time value checks or initial setup. ```ts T ``` ### Attribute * key The identifier for the attribute. Each key must be unique within the set of attributes on the cart or checkout. If you call \`applyAttributeChange()\` with a key that already exists, then the existing value is replaced. ```ts string ``` * value The value associated with the attribute key. This is a freeform string that can store any information the buyer or app provides. Attribute values are always strings. To store structured data, serialize it to JSON and parse it when reading. ```ts string ``` ### Methods The [`shopify` global object](https://shopify.dev/docs/api/checkout-ui-extensions/latest#target-apis-define-what-your-extension-does) provides methods to modify attribute data. Access the following methods on `shopify` to apply attribute changes. Available to `purchase.checkout` extension targets. * **applyAttributeChange** **(change: AttributeChange) => Promise\** **required** **deprecated** Updates or removes an attribute on the cart and checkout. On success, the [`attributes`](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/attributes#properties-propertydetail-attributes) property updates to reflect the change. **Note:** This method returns an error if the \cart instruction\ \\attributes.can\Update\Attributes\\ is false, or the buyer is using an accelerated checkout method, such as Apple Pay or Google Pay. **Deprecated:** Use cart metafields instead. ### AttributeChange The input for \`applyAttributeChange()\`. Pass either an \`AttributeUpdateChange\` (with \`type: 'updateAttribute'\`) to set the attribute or an \`AttributeRemoveChange\` (with \`type: 'removeAttribute'\`) to delete it. ```ts AttributeUpdateChange | AttributeRemoveChange ``` ### AttributeUpdateChange Updates an attribute on the cart and checkout. If an attribute with the provided key doesn't already exist, then it gets created. * key The key of the attribute to add or update. If an attribute with this key already exists, then its value is replaced. ```ts string ``` * type Identifies this as an attribute update or creation. Set this when creating a change to add or replace an attribute value. ```ts 'updateAttribute' ``` * value The new value for the attribute. ```ts string ``` ### AttributeRemoveChange Removes an attribute from the checkout. Pass this to \`applyAttributeChange()\` to delete an attribute by key. If the key doesn't exist, then the change has no effect. * key The key of the attribute to remove. ```ts string ``` * type Identifies this as an attribute removal. Set this when creating a change to delete an attribute by key. ```ts 'removeAttribute' ``` ### AttributeChangeResult The result of calling \`applyAttributeChange()\`. Use the \`type\` property to determine whether the change succeeded or failed. ```ts AttributeChangeResultSuccess | AttributeChangeResultError ``` ### AttributeChangeResultSuccess The result of a successful attribute change. The \`type\` property is \`'success'\`. * type Indicates that the attribute change was applied successfully. ```ts 'success' ``` ### AttributeChangeResultError The result of a failed attribute change. Check the \`message\` property for details about what went wrong. * message A message that explains the error. This message is useful for debugging. It isn't localized and shouldn't be displayed to the buyer. ```ts string ``` * type Indicates that the attribute change couldn't be applied. Check the \`message\` property for details. ```ts 'error' ``` ### Available Preact hooks The following Preact hooks provide a convenience wrapper that makes it easier to perform common tasks without writing your own logic. ## use​Attribute​Values(**[keys](#useattributevaluesgeneratedtype-propertydetail-keys)**​) The `useAttributeValues` hook returns an array of attribute values for the specified keys. Use this hook when you need to read the values of only some of the cart's attributes and you know the values' keys. ### Parameters * **keys** **string\[]** **required** An array of attribute keys. ### Returns * **(string | undefined)\[]** Examples ### Examples * #### ##### Description Read the full cart attribute list by mapping over \`shopify.attributes.value\`, which resolves to an array of key-value pairs. Use this pattern when you need every attribute on the cart, such as for a summary, or when keys aren't known ahead of time. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; export default function extension() { render(, document.body); } function Extension() { const attributes = shopify.attributes.value; if (attributes.length === 0) { return ( No custom cart attributes. ); } return ( Cart attributes {attributes.map((attribute) => ( {attribute.key}: {attribute.value} ))} ); } ``` * #### ##### Description Show content based on specific cart attribute values. This example uses \`useAttributeValues\` to retrieve two attributes and conditionally displays the buyer's t-shirt selection. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useAttributeValues} from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const [buyerSelectedFreeTShirt, tshirtSize] = useAttributeValues([ 'buyerSelectedFreeTShirt', 'tshirtSize', ]); if (Boolean(buyerSelectedFreeTShirt) === true) { return ( You selected a free t-shirt, size:{' '} {tshirtSize} ); } return null; } ``` * #### ##### Description Add or remove a cart attribute based on buyer interaction. This example reads the current \`giftWrap\` attribute and uses \`shopify.applyAttributeChange\` to toggle it, checking \`instructions.attributes.canUpdateAttributes\` before rendering the button. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useAttributeValues} from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const [giftWrapValue] = useAttributeValues([ 'giftWrap', ]); const giftWrap = Boolean(giftWrapValue); async function toggleGiftWrap() { const result = giftWrap ? await shopify.applyAttributeChange({ type: 'removeAttribute', key: 'giftWrap', }) : await shopify.applyAttributeChange({ type: 'updateAttribute', key: 'giftWrap', value: 'true', }); if (result.type === 'error') { console.error(result.message); } } return ( Gift wrapping:{' '} {giftWrap ? 'Added' : 'Not set'} {giftWrap ? 'Remove gift wrap' : 'Add gift wrap'} ); } ``` *** ## Best practices * **Check `canUpdateAttributes` before rendering mutation UI**: Use the [`instructions`](https://shopify.dev/docs/api/checkout-ui-extensions/apis/cart-instructions) property on the global `shopify` object to verify `instructions.attributes.canUpdateAttributes` before showing attribute controls. If it's `false`, hide the feature entirely. * **Use descriptive, namespaced keys**: Choose clear, unique attribute keys to avoid collisions with other extensions. For example, use `myApp_giftWrap` rather than `gift`. *** ## Limitations * Attribute changes aren't available when the buyer uses an accelerated checkout method such as Apple Pay or Google Pay. ***