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