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.
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};
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.
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 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.
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";23class ProductCard extends OverrideElement{4 render(){5 const{payload, api} = this.props || {};6 // Build your DOM here. Escape every agent-supplied value.7}8}910customElements.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:
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:
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:
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.