---
title: Attributes API
description: >-
  Attach custom key-value pairs to the cart for order metadata such as gift
  notes, delivery instructions, or app-specific data.
api_version: 2026-07-rc
source_url:
  html: >-
    https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/target-apis/checkout-apis/attributes-api
  md: >-
    https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/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/2026-07-rc/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-07-rc/targets/checkout/address#format-a-selected-suggestion-)
* [purchase.​address-autocomplete.​suggest](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/address#suggest-address-completions-)
* [purchase.​checkout.​actions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/navigation#navigation-target)
* [purchase.​checkout.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/block#block-target)
* [purchase.​checkout.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/order-summary#line-item-targets)
* [purchase.​checkout.​cart-line-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/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-07-rc/targets/checkout/information#information-target)
* [purchase.​checkout.​delivery-address.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#render-after-delivery-address-)
* [purchase.​checkout.​delivery-address.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#delivery-address-targets)
* [purchase.​checkout.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/footer#footer-target)
* [purchase.​checkout.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/header#header-target)
* [purchase.​checkout.​payment-method-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/payment#render-after-payment-methods-)
* [purchase.​checkout.​payment-method-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/payment#payment-targets)
* [purchase.​checkout.​pickup-location-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/local-pickup#render-after-pickup-locations-)
* [purchase.​checkout.​pickup-location-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/local-pickup#location-list-targets)
* [purchase.​checkout.​pickup-location-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/local-pickup#location-option-item-target)
* [purchase.​checkout.​pickup-point-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/pickup-points#render-after-pickup-points-)
* [purchase.​checkout.​pickup-point-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/pickup-points#pickup-points-targets)
* [purchase.​checkout.​reductions.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/order-summary#checkout-reductions-after-)
* [purchase.​checkout.​reductions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/order-summary#reductions-targets)
* [purchase.​checkout.​shipping-option-item.​details.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#shipping-option-item-targets)
* [purchase.​checkout.​shipping-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#render-after-shipping-option-)
* [purchase.​checkout.​shipping-option-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#render-after-shipping-options-)
* [purchase.​checkout.​shipping-option-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/checkout/shipping#shipping-option-list-targets)
* [purchase.​thank-you.​announcement.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/thank-you/announcement#thank-you-announcement-)
* [purchase.​thank-you.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/thank-you/block#block-target)
* [purchase.​thank-you.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/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-07-rc/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-07-rc/targets/thank-you/information#information-target)
* [purchase.​thank-you.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/thank-you/footer#footer-target)
* [purchase.​thank-you.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/targets/thank-you/header#header-target)

### Properties

The [`shopify` global object](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc#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\<Attribute\[]>**

  **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<void>
  ```

* 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/2026-07-rc#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\<AttributeChangeResult>**

  **required**

  **deprecated**

  Updates or removes an attribute on the cart and checkout. On success, the [`attributes`](https://shopify.dev/docs/api/checkout-ui-extensions/2026-07-rc/apis/attributes#properties-propertydetail-attributes) property updates to reflect the change.

  **Note:** This method returns an error if the \<a href="/docs/api/checkout-ui-extensions/2026-07-rc/apis/cart-instructions#properties-propertydetail-instructions">cart instruction\</a> \<code>\<span class="PreventFireFoxApplyingGapToWBR">attributes.can\<wbr/>Update\<wbr/>Attributes\</span>\</code> 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(<Extension />, document.body);
  }

  function Extension() {
    const attributes = shopify.attributes.value;

    if (attributes.length === 0) {
      return (
        <s-text>
          No custom cart attributes.
        </s-text>
      );
    }

    return (
      <s-stack>
        <s-text>Cart attributes</s-text>
        {attributes.map((attribute) => (
          <s-text key={attribute.key}>
            {attribute.key}: {attribute.value}
          </s-text>
        ))}
      </s-stack>
    );
  }
  ```

* ####

  ##### 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(<Extension />, document.body);
  }

  function Extension() {
    const [buyerSelectedFreeTShirt, tshirtSize] =
      useAttributeValues([
        'buyerSelectedFreeTShirt',
        'tshirtSize',
      ]);

    if (Boolean(buyerSelectedFreeTShirt) === true) {
      return (
        <s-text>
          You selected a free t-shirt, size:{' '}
          {tshirtSize}
        </s-text>
      );
    }

    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(<Extension />, 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 (
      <s-stack>
        <s-text>
          Gift wrapping:{' '}
          {giftWrap ? 'Added' : 'Not set'}
        </s-text>
        <s-button
          onClick={toggleGiftWrap}
          disabled={
            !shopify.instructions.value.attributes
              .canUpdateAttributes
          }
        >
          {giftWrap
            ? 'Remove gift wrap'
            : 'Add gift wrap'}
        </s-button>
      </s-stack>
    );
  }
  ```

***

## 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.

***
