# Checkout extension reference

> Reference for how a Shoplazza checkout extension works: the extend function, the HTML template it renders, and hiding native checkout modules with deleteTarget.

This page covers the mechanics of a checkout extension: the `extend` function, the HTML template it renders, and how to hide native checkout modules. The extension points themselves are listed in [Extension points](/docs/app/extensions/checkout/extension-points), and the data and event APIs in [CheckoutAPI reference](/docs/app/extensions/checkout/checkout-api). For the development workflow, see [Create a Checkout Extension](/docs/app/extensions/checkout/add-checkout-extension). For practical scenarios, see [Checkout Extension Recipes](/docs/app/extensions/checkout/extension-recipes).

## Extension points

Extension points define where custom content is inserted into the page. Specify the corresponding name in the `extensionPoint` field of `extend()`, and the content is rendered at that location.

The checkout page and the thank-you page each offer a fixed set of extension points, grouped by the area of the page they render in. Some of them are dynamic: they render once per line item, per shipping line or per dialog, so the name carries the id of that row and has to be built at runtime.

For the full list, the area each point belongs to, and the native modules you can hide alongside it, see [Extension points](/docs/app/extensions/checkout/extension-points).

## Hiding native modules (deleteTarget)

`deleteTarget` hides native modules on the checkout page, such as section titles, the address book, or the step breadcrumb.

Declare it in an `extension.json` file placed in the extension directory, alongside `shoplazza.extension.toml`:

```text
extensions/
└── my-checkout/
    ├── shoplazza.extension.toml
    ├── extension.json            # Only deleteTarget is written by you
    ├── package.json
    └── src/
```

**`extension.json`**:

```json
{ "deleteTarget": ["contactInformationHeader", "shippingAddressHeader"] }
```

To apply the configuration:

1. Upgrade the Shoplazza CLI to 2.0.10 or later.
2. Restart `shoplazza app dev` to preview, or run `shoplazza app deploy` to publish. `shoplazza app dev` does not watch `extension.json`, so restart it after every change to the file.
3. To show the modules again, set `"deleteTarget": []` and deploy once more.
4. The CLI writes `extensionId` and `version` back into `extension.json`. Keep both fields — do not fill them in by hand and do not delete them.

### Available targets

| Target | Location | Description |
|---|---|---|
| `header` | Page header | Store name and logo area at the top |
| `navigate` | Page header | Step breadcrumb, such as Information, Shipping, Payment |
| `loginOrLogout` | Page header | Log in / log out link |
| `returnBtn` | Page header | Return link, rendered next to the submit button |
| `contactInformation` | Contact information | The whole contact information module |
| `contactInformationHeader` | Contact information | The "Contact" title |
| `contactEmail` | Contact information | Email input |
| `contactPhone` | Contact information | Phone number input |
| `emailOrPhone` | Contact information | Combined email or phone input, used when the store accepts either |
| `contactSubscribe` | Contact information | Marketing email subscription checkbox |
| `shippingAddress` | Address | The whole shipping address module |
| `shippingAddressHeader` | Address | The "Shipping address" title |
| `shippingAddressBook` | Address | Address book for picking a saved address |
| `securityIdentifier` | Address | Security and privacy identifier |
| `addressCard` | Address | Filled-in information card, shown on the shipping and payment steps |
| `billingAddress` | Address | Billing address module on the payment step |
| `deliveryMethod` | Delivery and pickup | Delivery method module that switches between shipping and pickup |
| `deliveryMethodHeader` | Delivery and pickup | Delivery method title |
| `shippingList` | Delivery and pickup | Shipping rate list |
| `shippingLinesTitle` | Delivery and pickup | Shipping rate list title |
| `pickupInformation` | Delivery and pickup | Pickup information module |
| `pickupInformationHeader` | Delivery and pickup | Pickup information title |
| `pickupAddress` | Delivery and pickup | Pickup location address list |
| `pickupAddressHeader` | Delivery and pickup | Pickup location address title |
| `paymentHeader` | Payment | Payment method list title |
| `payPalExpress` | Payment | PayPal express checkout button |
| `infoSubmit` | Submit buttons | "Continue to shipping" on the information step |
| `shippingSubmit` | Submit buttons | "Continue to payment" on the shipping step |
| `paymentSubmit` | Submit buttons | "Place order" on the payment step |
| `orderSummary` | Order summary | The whole order summary, in the right column on desktop and at the top on mobile |
| `orderSummaryHeader` | Order summary | Order summary title, collapsible on mobile |
| `productList` | Order summary | Line item list |
| `ProductListCover` | Order summary | Line item thumbnail |
| `productListSkuProperties` | Order summary | Custom line item properties, rendered only when `properties` is not empty |
| `priceList` | Price breakdown | The whole price breakdown |
| `priceListSubTotal` | Price breakdown | Subtotal |
| `priceListShippingTotal` | Price breakdown | Shipping |
| `priceListTaxTotal` | Price breakdown | Tax |
| `priceListShippingTaxTotal` | Price breakdown | Shipping tax |
| `priceListGiftCard` | Price breakdown | Gift card deduction |
| `totalPrice` | Price breakdown | Total |
| `reductions` | Discounts and notes | Discount code and gift card input area |
| `mobileCoupon` | Discounts and notes | Discount code input on mobile, hidden on desktop |
| `giftCardTag` | Discounts and notes | Applied discount code or gift card tag, with a remove button |
| `specialInstruction` | Discounts and notes | Order note input on the shipping step |
| `thankyouHeader` | Thank-you page | Checkout completion header |
| `thankyouContent` | Thank-you page | Checkout completion content |
| `thankyouFooter` | Thank-you page | Checkout completion footer |
| `thankyouShippingInfo` | Thank-you page | Shipping information block, shown for orders that ship |
| `thankyouFailOrderStatus` | Thank-you page | Failure message shown when the order is cancelled or has failed |
| `thankyouPageGiftCardAddress` | Thank-you page | Billing address and gift card information, shown for virtual-product orders |
| `thankyouPagePickupAddress` | Thank-you page | Pickup address, shown for pickup orders |

## CheckoutAPI reference

`CheckoutAPI` is the global object the checkout platform mounts on `window.CheckoutAPI`, on the checkout page and on the thank-you page. Through it an extension reads the order and the choices the buyer has made, listens for those values changing, and drives part of the checkout UI.

Its methods are grouped into namespaces: `address`, `base`, `config`, `coupon`, `exception`, `extension`, `order`, `payment`, `pickup`, `step`, `store`, `summary`, `track`, `user` and `utils`.

For every namespace, the type definitions, and the rules that apply before you call anything — when the object is mounted, how to pair `on*` with `remove*`, and why `register*` methods are not listeners — see [CheckoutAPI reference](/docs/app/extensions/checkout/checkout-api).

## extend function and HTML template

### extend function

`extend` is the core function for registering content to an extension point. It is imported from `shoplazza-extension-ui`:

```javascript
import { extend } from 'shoplazza-extension-ui';

extend({
  extensionPoint: 'Checkout::Navigate::RenderBefore',
  component: '<h1>Hello, Shoplazza!</h1>',
});
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `extensionPoint` | `string` | Yes | Where the content renders. See [Extension points](/docs/app/extensions/checkout/extension-points). |
| `component` | `string` or `Promise<string>` | Yes | The HTML to render. A plain string works on its own; a `Promise<string>` requires `type: 'spz'`. |
| `type` | `'spz'` | No | Leave it out for the minimal form. `'spz'` turns on the LessJS component library and the placeholder substitution described below. |
| `localeMap` | `object` | No | Messages for the `${i18n('key')}` placeholders in `component`, keyed by locale. Only used when `type` is `'spz'`. |
| `id` | `string` | No | The extension id. Pass `__EXTENSION_ID__`, which the CLI replaces at build time with your extension's name. |

Without `type`, `component` is an HTML string rendered as is. That is the minimal form shown above, and it is all most extensions need.

With `type: 'spz'`, the SDK does three extra things before rendering: it loads the LessJS component library, it accepts a `Promise<string>` as `component`, and it substitutes the placeholders in your HTML with `localeMap`:

```javascript
import { extend } from 'shoplazza-extension-ui';

extend({
  type: 'spz',
  extensionPoint: 'Checkout::Navigate::RenderBefore',
  component: Promise.resolve(
    "<div>${i18n('greeting')} - ${i18n('promo.line')}</div>"
  ),
  localeMap: {
    'en-US': { greeting: 'Hello', promo: { line: 'Free returns within 30 days' } },
    'zh-CN': { greeting: '你好', promo: { line: '30 天内免费退货' } },
  },
  id: __EXTENSION_ID__,
});
```

- The key inside `${i18n(...)}` **must be quoted**: `${i18n('greeting')}` is substituted, while `${i18n(greeting)}` renders as `undefined`.
- Nested keys are flattened with a dot, so `promo: { line: … }` is read as `${i18n('promo.line')}`.
- Which map is used depends on the buyer's full locale, the key of each entry in `localeMap`.
- Write the HTML as an ordinary quoted string, not a template literal, so that JavaScript leaves the `${…}` placeholders for the SDK to substitute.
- The placeholder `{EXTENSION_POINT}` is substituted with an id derived from the extension point name, for example `Checkout-Summary-RenderBefore-ID` for `Checkout::Summary::RenderBefore`.

:::warning
**Security Note (XSS Risk):** `component` is a **directly rendered HTML string**. **Do not concatenate unsanitized user input or external API return values into it** to avoid XSS injection. For any dynamic content, perform HTML escape first, or use DOM APIs like `createElement` / `textContent` instead.

**Multiple calls to the same extension point:** The same `extensionPoint` can be `extend`-ed multiple times. Content will be **appended** in call order, not overwritten.
:::

### HTML template

For complex content, we recommend splitting HTML into separate files. The scaffolding generates the following file structure by default:

**`src/index.html`** (optional, assembly entry):

```html
<div>
  import './style.html'
  import './content.html'
  import './script.html'
</div>
```

:::note
**`import './xxx.html'` is not ES Module syntax.** It is a **text concatenation directive** recognized by `shoplazza-cli` at build time. During the bundling phase, the CLI replaces the `import './xxx.html'` line with the actual content of `./xxx.html` (inlined in order of appearance), rather than loading it dynamically at runtime.

- This syntax **can only be used at the top level of `src/index.html`** as an assembly entry. It will not be recognized inside `<script>` tags or in JS files.
- Imported HTML fragments should not contain further `import` directives to avoid recursion.
- For simple content or scenarios requiring dynamic HTML generation, you can skip template files and directly pass concatenated strings to `component` in `index.js`.
:::

**`src/style.html`** (optional, styles):

```html
<style>
  .my-block {
    padding: 12px;
    background: #f5f5f5;
  }
</style>
```

**`src/content.html`** (optional, page content):

```html
<div class="my-block">
  <h2>Hello, Shoplazza!</h2>
</div>
```

**`src/script.html`** (optional): Scaffolding placeholder. To write scripts within an HTML template fragment, use a regular `<script>` tag:

```html
<script>
  document.querySelector('.my-block').addEventListener('click', function() {
    window.CheckoutAPI.step.goToHomePage();
  });
</script>
```

You can also skip HTML templates entirely and construct HTML strings directly in `index.js` for `component`. This suits simple content or dynamic generation.

:::caution Checkout page performance budget
Checkout page performance directly impacts conversion rate. Keep your extension JS/CSS as lean as possible: aim for `< 30KB` gzipped per extension, avoid large dependencies, and prefer passive approaches like `setTimeout` / `IntersectionObserver` over high-frequency polling.
:::
