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.

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

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.

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
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
query CartDeliveryOptionsTransformRunInput {
  cart {
    deliveryGroups {
      deliveryAddress {
        countryCode
      }
      deliveryOptions {
        handle
        title
      }
    }
  }
  deliveryCustomization {
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
src/cart_delivery_options_transform_run.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
{
  "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
{ "operations": [] }
tests/not-configured.input.json
{
  "cart": {
    "deliveryGroups": [
      { "deliveryAddress": { "countryCode": "US" }, "deliveryOptions": [{ "handle": "51c2-standard", "title": "Standard" }] }
    ]
  },
  "deliveryCustomization": { "metafield": null }
}
tests/not-configured.output.json
{ "operations": [] }
tests/notes-added.input.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
{
  "operations": [
    { "deliveryOptionRename": { "deliveryOptionHandle": "51c2-standard", "title": "Standard · 3–5 business days" } },
    { "deliveryOptionRename": { "deliveryOptionHandle": "51c2-express", "title": "Express · next business day" } }
  ]
}

Written by Lintel. MIT license: free to use in your own store and in client work.

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.
  • Written for Function API version 2026-10 and checked against Shopify’s reference on the date shown. Shopify releases a new version every quarter.
  • Custom apps that contain Functions only run on Shopify Plus stores; a public app with the same Function runs on any plan unless the page says otherwise.
  • Every example ships with test fixtures, and we run them before publishing. Run them again after you change the code: shopify app function run.
  • MIT license. Use the code in your own store or in client work.

Need this built, tested and maintained for your store? See Shopify Functions development.

Have something to build? Let’s talk it through.

Taking a limited number of projects at a time. Every brief is read by an engineer, not a salesperson.

Gencer KarakayaShopify Engineer

An engineer reads your brief, and we reply by email.