# Override Commerce Client UI Components

Use the client's Component Override SDK to replace selected parts of the client UI, such as product card displays, order status, loyalty widgets, and appointment pickers to match your storefront branding. The client still handles specific aspects of the client's display: you can't replace the header, search bar, and follow-up suggestions with overrides.

You can continue to update the client with new releases, but you're responsible for maintaining custom displays and testing them.

:::note
Overrides apply to all storefront types that support the client.
:::

An override is a key and tag name mapping for a custom element. This mapping tells the widget which custom UI to use, but first register the custom element with `customElements.define()`. After it's registered, declare the override by pairing an override key with a tag name in the map.

When the widget renders that component, it calls `document.createElement("cx-product-card")` and assigns a single `props` object to the element's `props` setter.

For example, you build and register `cx-product-card`, then add it to the map.

Example:

```js
window.ComponentOverrides = {
  // Standard built-in custom components
  ProductTile: "cx-product-card",
  ProductCarousel: "product-carousel",
  ProgressSteps: "progress-steps",
  // Custom agent-action block, matched by output_name
  recentOrders: "cx-recent-orders",
};
```

The example also includes other standard custom components. Every other property is a custom property that's matched against a streaming `tool_results` block by its top-level `output_name`. See [Create Overrides for Custom Agent Actions](#create-overrides-for-custom-agent-actions). If you leave a built-in property blank, the widget uses the default.

:::note
A tag name must be a valid custom element name: lowercase and containing a hyphen. Anything else, such as `"ProductTile"` or `"div"` is rejected with a console error, and the component is left blank.
:::

## How Overrides Receive Data

The widget automatically passes a shared `props` data object to all overrides. To capture this data, define a props setter function in your custom code. Whether your override sits inside a standard product component or a custom block, the SDK assigns the override to your element's `props` property.

```js
element.props = { name, payload, api };
```

| **Property** | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`       | A string that identifies the matching override property, such as `ProductTile` , `ProductCarousel`, `ProductDetailCard`. For a custom block, it contains that block's output_name.                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `payload`    | Raw, un-normalized block data. Because the structure varies depending on the block type, its contents match one of these schemas: **Product blocks:** Product details are in `payload.data`, including `name`, `price`, `discountPrice`, `currencyCode`, `imageUrl`, and `productPageUrl`. Custom catalog fields are in `payload.customProperties.c_`*. **Carousel blocks:** Products are in the `payload.products` list. Loop through this list to access individual product objects, using the same structure as a product block. **Progress blocks:** Contain a list of agent progress updates in `payload.steps`. The last item is the current update. |
| `api`        | A set of helpers that communicate to the agent and read the widget's current state. It gives the override a way to interact with the agent and adapt to the widget's presentation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

The `api` object contains these same properties:

| **Property**                  | **Type**                        | **Description**                                                                                                                                                                                                                               |
| ----------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sendMessage(text, options?)` | Function                        | Post a message to the agent on the override's behalf. This property passes one option, silent, to suppress the outgoing user bubble for a machine-readable payload. For example: `api.sendMessage(JSON.stringify(record), { silent: true });` |
| `isLoading`                   | Boolean (`true` or `false`)     | Indicates whether the widget is waiting for a response after sending a standard agent request.                                                                                                                                                |
| `isConnected`                 | Boolean (`true` or `false`)     | Indicates whether the widget has an active connection to the Agentforce backend.                                                                                                                                                              |
| `componentType`               | `"chat"`, `"dialog"`, `"modal"` | The widget's presentation format. Use it to adjust your override's layout and styling for inline chat, dialog, or modal experiences.                                                                                                          |
| `mode`                        | `"standalone"` or `"embedded"`  | Indicates whether the widget is the page's primary interface (standalone) or appears alongside other storefront UI (embedded).                                                                                                                |

## Customizable Components on the Commerce Client Widget UI

You can replace components of the shopper-facing Commerce Client Widget UI, primarily the chat window and the content it renders during a conversation. Replace built-in UI elements, such as product cards, carousels, product-detail cards, and custom agent-action output blocks.

:::note
You can't override the widget chrome header, search bar, and follow-up suggestions. Those properties are accepted by the map but have no render path, so they're ignored. Style chrome with theme properties and custom CSS instead. See [Customize the Agentforce Commerce Client](/docs/commerce/b2c-commerce/guide/customize-commerce-client.md).
:::

| **Override Property** | **Replaces**                                                                                                                                                                                                                                                                                                                                 | **Payload Custom Component Receives**        |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `ProductTile`         | An individual product card inside a carousel                                                                                                                                                                                                                                                                                                 | One raw product                              |
| `ProductCarousel`     | The whole carousel (title plus cards). If you override `ProductCarousel`, your `ProductTile` override isn't used inside it. You own everything the carousel renders.                                                                                                                                                                         | `{ title, products }`                        |
| `ProductDetailCard`   | The in-chat product detail card If you override `ProductDetailCard`, it replaces the **entire** built-in card, including its express-checkout section. When express checkout is enabled on the storefront, the built-in card renders Apple Pay and Google Pay buttons in an iframe served from a Salesforce Payments URL on your storefront. | One raw product-detail block, `{ id, data }` |
| `ProductComparison`   | The in-chat product comparison table                                                                                                                                                                                                                                                                                                         | `{ products, table }`                        |
| `ProgressSteps`       | The "Loading Step N" panel shown while an agent action streams                                                                                                                                                                                                                                                                               | `{ steps }`                                  |
| Any other property    | A custom agent-action block matched by `output_name`                                                                                                                                                                                                                                                                                         | Raw `data`                                   |

Your override receives that URL at `payload.data.expressPaymentUrl` and must build its own checkout UI from it (the built-in card loads it in an iframe). If `expressPaymentUrl` is absent, the product isn't eligible for express-checkout, so don't render a payment button.

:::note
Express checkout is a server-side feature. It requires the `plugin_salesforcepayments` cartridge (SFRA) and express checkout enabled on the storefront before `expressPaymentUrl` appears in the payload. Overriding `ProductDetailCard` only changes how the card, and the express button within it, is rendered.
:::

## Create Overrides

Author each override as a custom element with a `props` setter that drives rendering. The messaging entry exports an optional `OverrideElement` base class that implements the props setter, getter, and lifecycle.

```js
import { OverrideElement } from "@cimulate/copilot-widget/messaging";

class ProductCard extends OverrideElement {
  render() {
    const { payload, api } = this.props || {};
    // Build your DOM here. Escape every agent-supplied value.
  }
}

customElements.define("product-card", ProductCard);
```

Script-tag consumers reach the same class as `CimulateMessaging.OverrideElement` on the UMD bundle. You can also implement the contract directly on `HTMLElement`: a `props` setter that stores the value and calls your render, a matching getter, and a `connectedCallback` that renders.

The widget persists your element instance across its own re-renders. `connectedCallback` fires once; on later renders, the widget assigns to your `props` setter in place, so instance state, listeners, and focus survive. An unregistered tag name is rebuilt on every commit instead, which is why registering the element matters.

### Safely Render Agent and Catalog Data

The widget passes data without sanitizing it. Your override is responsible for rendering that data safely.

You own the DOM your override produces. If you create HTML strings, escape every value that comes from the agent or catalog before inserting them with `innerHTML`. Build structure as author-constant strings and pass data through an escaping helper. Don't put an unescaped value into `innerHTML`.

Before assigning agent-supplied values to `href` or `src`, validate that they use an allowed URL scheme, such as https:. Reject unsafe schemes such as javascript: and data: unless your use case explicitly and safely supports them.

### Register Your Custom Element Safely

Calling `customElements.define()` for a tag that is already registered causes an error. If the error is not handled, the script stops before it assigns `window.CimulateOverrides`, and the widget uses its default rendering instead of your overrides.

Before registering your custom element, check whether the tag is already defined:

```js
function defineOnce(tag, ctor) {
  if (!customElements.get(tag)) customElements.define(tag, ctor);
}

defineOnce("product-card", ProductCard);
```

### Create Overrides for Custom Agent Actions

In addition to the named product components, you can create overrides for custom agent-action blocks, such as order tracking, option pickers, warranty forms, and surveys.

Add the custom block's `output_name` as a property in the overrides map. When the agent invokes the corresponding action, it sends a single `tool_results` block to your override:

```json
{ "type": "tool_results", "index": 0, "output_name": "recentOrders", "data": {} }
```

The widget recognizes the block by its top-level `type`, routes on `output_name` alone, and renders the element registered for that key. An unmatched `output_name` renders nothing.

:::note
The widget doesn't route on a field inside `data`, so agent JSON can't hijack routing.
:::

Your override receives only the raw `data` as `payload`. The widget removes its own internal fields before passing it through. Although the data arrives as a token stream, the widget waits until the JSON is complete. Each render therefore receives one complete payload, not a partial object.

For example, an agent action that returns a shopper's recent orders emits a block with `output_name: "recentOrders"` and an orders array in `data`. Register a custom element for that property and render the array:

```js
import { OverrideElement } from "@cimulate/copilot-widget/messaging";

class RecentOrders extends OverrideElement {
  render() {
    const { payload } = this.props || {};
    const orders = payload?.orders ?? [];
    // Build rows from `orders`. Escape every agent-supplied value.
  }
}

defineOnce("cx-recent-orders", RecentOrders);

window.CimulateOverrides = {
  ...window.CimulateOverrides,
  recentOrders: "cx-recent-orders",
};
```

The `data` the agent sent (here, `{ orders: [...] }`) arrives as `payload`, so `payload.orders` is the array to render. The property in the map (`recentOrders`) must match the block's `output_name` exactly.

### Handle Override Errors

Errors are isolated to the affected component. If creating an override or assigning its `props` causes an error, that component renders blank while the rest of the widget continues to render. The widget also logs a warning that begins with `[CimulateOverrides]`. The rest of the transcript is unaffected and the widget doesn't crash.
