# Add a delivery note to shipping option names

> A Shopify delivery customization Function that appends a delivery note such as “3–5 business days” to shipping option names, by title and country.

Page: https://lintelagency.com/resources/shopify-functions/rename-shipping-options-with-delivery-note

## In short

This delivery customization Function adds a short note to shipping option names at checkout, such as “Standard · 3–5 business days”. Rules match the start of the option title and can be limited to countries; they live in a JSON metafield. It uses the deliveryOptionRename operation of the cart.delivery-options.transform.run target, Delivery Customization API version 2026-10.

## Facts

- **Function API:** Delivery Customization
- **Target:** cart.delivery-options.transform.run
- **Plan:** Any plan in a public app; Shopify Plus in a custom app
- **App scope:** write_delivery_customizations
- **API version:** 2026-10
- **Last checked:** 2 November 2026

## How to use it

1. Create an app with the Shopify CLI, then add the extension: shopify app generate extension --template delivery_customization, with JavaScript.
2. Replace the generated shopify.extension.toml and the two files in src/ with the files below.
3. Run the fixtures with shopify app function run --input tests/notes-added.input.json.
4. Deploy with shopify app deploy and create the customization with deliveryCustomizationCreate.
5. Set the rules in the $app:function-configuration metafield, for example {"rules": [{"title": "Standard", "note": "3–5 business days"}]}.

## Code

### shopify.extension.toml

```toml
api_version = "2026-10"

[[extensions]]
name = "Add a delivery note to shipping options"
handle = "shipping-option-delivery-note"
type = "function"

  [[extensions.targeting]]
  target = "cart.delivery-options.transform.run"
  input_query = "src/cart_delivery_options_transform_run.graphql"
  export = "cart-delivery-options-transform-run"

  [extensions.build]
  command = ""
  path = "dist/function.wasm"
```

### src/cart_delivery_options_transform_run.graphql

```graphql
query CartDeliveryOptionsTransformRunInput {
  cart {
    deliveryGroups {
      deliveryAddress {
        countryCode
      }
      deliveryOptions {
        handle
        title
      }
    }
  }
  deliveryCustomization {
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
```

### src/cart_delivery_options_transform_run.js

```js
// @ts-check

/**
 * @typedef {import("../generated/api").CartDeliveryOptionsTransformRunInput} CartDeliveryOptionsTransformRunInput
 * @typedef {import("../generated/api").CartDeliveryOptionsTransformRunResult} CartDeliveryOptionsTransformRunResult
 */

/**
 * Settings stored as JSON in the customization's metafield, so delivery
 * promises can change (holidays, carrier delays) without a deploy.
 */
const DEFAULT_CONFIG = {
  /**
   * Rules checked in order; the first one whose title matches wins.
   * `title` is matched case-insensitively against the start of the option
   * title; `countries` (optional) limits the rule to delivery addresses there.
   * @type {{title: string, note: string, countries?: string[]}[]}
   */
  rules: [],
  // Placed between the original title and the note.
  separator: " · ",
};

/**
 * @param {CartDeliveryOptionsTransformRunInput} input
 * @returns {CartDeliveryOptionsTransformRunResult}
 */
export function cartDeliveryOptionsTransformRun(input) {
  const config = { ...DEFAULT_CONFIG, ...input.deliveryCustomization?.metafield?.jsonValue };
  const operations = [];

  for (const group of input.cart.deliveryGroups) {
    const country = group.deliveryAddress?.countryCode ?? null;
    for (const option of group.deliveryOptions) {
      const title = option.title ?? "";
      const rule = config.rules.find(
        (r) =>
          title.toLowerCase().startsWith(r.title.toLowerCase()) &&
          (!r.countries?.length || (country !== null && r.countries.some((code) => code.toUpperCase() === country))),
      );
      // Keep the original title in front: Shopify adds the carrier name before
      // it anyway, and customers recognise the rate they saw on the cart page.
      if (rule) {
        operations.push({
          deliveryOptionRename: { deliveryOptionHandle: option.handle, title: `${title}${config.separator}${rule.note}` },
        });
      }
    }
  }

  return { operations };
}
```

## Test input and expected output

### tests/country-rule-skipped.input.json

```json
{
  "cart": {
    "deliveryGroups": [
      {
        "deliveryAddress": { "countryCode": "CA" },
        "deliveryOptions": [{ "handle": "77aa-express", "title": "Express" }]
      }
    ]
  },
  "deliveryCustomization": {
    "metafield": {
      "jsonValue": {
        "rules": [
          { "title": "Express", "note": "next business day", "countries": ["US"] },
          { "title": "Standard", "note": "3–5 business days" }
        ]
      }
    }
  }
}
```

### tests/country-rule-skipped.output.json

```json
{ "operations": [] }
```

### tests/not-configured.input.json

```json
{
  "cart": {
    "deliveryGroups": [
      { "deliveryAddress": { "countryCode": "US" }, "deliveryOptions": [{ "handle": "51c2-standard", "title": "Standard" }] }
    ]
  },
  "deliveryCustomization": { "metafield": null }
}
```

### tests/not-configured.output.json

```json
{ "operations": [] }
```

### tests/notes-added.input.json

```json
{
  "cart": {
    "deliveryGroups": [
      {
        "deliveryAddress": { "countryCode": "US" },
        "deliveryOptions": [
          { "handle": "51c2-standard", "title": "Standard" },
          { "handle": "51c2-express", "title": "Express" },
          { "handle": "51c2-pickup", "title": "Pickup in store" }
        ]
      }
    ]
  },
  "deliveryCustomization": {
    "metafield": {
      "jsonValue": {
        "rules": [
          { "title": "Express", "note": "next business day", "countries": ["US"] },
          { "title": "Standard", "note": "3–5 business days" }
        ]
      }
    }
  }
}
```

### tests/notes-added.output.json

```json
{
  "operations": [
    { "deliveryOptionRename": { "deliveryOptionHandle": "51c2-standard", "title": "Standard · 3–5 business days" } },
    { "deliveryOptionRename": { "deliveryOptionHandle": "51c2-express", "title": "Express · next business day" } }
  ]
}
```

## Limits and edge cases

- For carrier rates Shopify puts the carrier name in front of the title and you can’t remove it; you can only change what follows.
- The first matching rule wins, so put country-specific rules before general ones.
- Delivery promises are text you control; nothing checks them against real carrier times. Update the metafield before holidays.

## Questions

### How do I show estimated delivery times at Shopify checkout?

A delivery customization Function can rename each option to include a delivery window, such as “Standard · 3–5 business days”, as this example does.

### Can the note change by country?

Yes. Give a rule a countries list, for example ["US"], and it only applies when the delivery address is in one of them.

### Can I add a delivery message to the product page too?

The Function only changes checkout. For the product page, use a theme snippet; see the journal article on adding a dynamic delivery message.

## Shopify documentation

- [Delivery Customization Function API](https://shopify.dev/docs/api/functions/latest/delivery-customization)

## Related

- [Activate a delivery, payment or validation Function](https://lintelagency.com/resources/graphql/register-function-customization)
- [Adding a Dynamic Delivery Message to a Shopify Theme](https://lintelagency.com/journal/adding-a-dynamic-delivery-message-to-a-shopify-theme)
- [Delivery customization: hide, rename and sort shipping options](https://lintelagency.com/journal/shopify-delivery-customization-function)
- [Hide shipping rates by product tag](https://lintelagency.com/resources/shopify-functions/hide-shipping-rates-by-product-tag)

License: MIT. Free to use in your own store and in client work.
