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.

Overrides apply to all storefront types that support the client.

Note

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:

1window.ComponentOverrides = {
2  // Standard built-in custom components
3  ProductTile: "cx-product-card",
4  ProductCarousel: "product-carousel",
5  ProgressSteps: "progress-steps",
6  // Custom agent-action block, matched by output_name
7  recentOrders: "cx-recent-orders",
8};

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. If you leave a built-in property blank, the widget uses the default.

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.

Note

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.

1element.props = { name, payload, api };
PropertyDescription
nameA string that identifies the matching override property, such as ProductTile , ProductCarousel, ProductDetailCard. For a custom block, it contains that block’s output_name.
payloadRaw, 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.
apiA 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:

PropertyTypeDescription
sendMessage(text, options?)FunctionPost 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 });
isLoadingBoolean (true or false)Indicates whether the widget is waiting for a response after sending a standard agent request.
isConnectedBoolean (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.

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.

Note

Override PropertyReplacesPayload Custom Component Receives
ProductTileAn individual product card inside a carouselOne raw product
ProductCarouselThe 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 }
ProductDetailCardThe 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 }
ProductComparisonThe in-chat product comparison table{ products, table }
ProgressStepsThe “Loading Step N” panel shown while an agent action streams{ steps }
Any other propertyA custom agent-action block matched by output_nameRaw 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.

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.

Note

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.

1import { OverrideElement } from "@cimulate/copilot-widget/messaging";
2
3class ProductCard extends OverrideElement {
4  render() {
5    const { payload, api } = this.props || {};
6    // Build your DOM here. Escape every agent-supplied value.
7  }
8}
9
10customElements.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:

1function defineOnce(tag, ctor) {
2  if (!customElements.get(tag)) customElements.define(tag, ctor);
3}
4
5defineOnce("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:

1{ "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.

The widget doesn’t route on a field inside data, so agent JSON can’t hijack routing.

Note

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:

1import { OverrideElement } from "@cimulate/copilot-widget/messaging";
2
3class RecentOrders extends OverrideElement {
4  render() {
5    const { payload } = this.props || {};
6    const orders = payload?.orders ?? [];
7    // Build rows from `orders`. Escape every agent-supplied value.
8  }
9}
10
11defineOnce("cx-recent-orders", RecentOrders);
12
13window.CimulateOverrides = {
14  ...window.CimulateOverrides,
15  recentOrders: "cx-recent-orders",
16};

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.