Build Rich UI Experiences with HXL Widgets

Widgets are platform-independent UI compositions. With widgets, you define rich, interactive interfaces once and deploy them across supported Salesforce applications and third-party platforms.

You build widgets by defining their composition in JSON files. Understand these core concepts to compose and deploy widgets.

Supported Applications and Platforms 

Widgets can be deployed to:

  • Agentforce Employee agent in Lightning Experience
  • ChatGPT (via Model Context Protocol)
  • Claude (via Model Context Protocol)
  • Slackbot (via Model Context Protocol)

Building Blocks of a Widget 

A widget consists of these building blocks.

  • Composition file defines the visual UI composition. It specifies which components render, their hierarchical nesting, ordering, and data bindings by using {!$attrs.attributeName} token expressions.
  • Widget configuration file defines the widget’s label, description, and type.
  • Schema file defines the attribute contract. It outlines the attributes and their associated Lightning types that the widget accepts, including validation rules, data types, and display metadata.

UiWidgetBundle Metadata Type 

The UiWidgetBundle metadata type describes the widgets. It’s available in API version 67.0 and later.

Understand the UiWidgetBundle Structure 

UiWidgetBundle components are stored in the uiWidgets folder and consist of a composition file, widget configuration file, and optional schema file. See UiWidgetBundle.

For a detailed example of this structure, see Example: Mapping a Hotel Card Widget.

Use Salesforce CLI or Metadata API to Deploy UiWidgetBundles 

To deploy a UiWidgetBundle to your Salesforce org, use Metadata API or Salesforce CLI commands. Metadata API uses a manifest file that defines the metadata that you want to deploy.

Here’s an example package.xml manifest file for a UiWidgetBundle that includes the widget hotelCard.

1<?xml version="1.0" encoding="UTF-8"?>
2<Package xmlns="http://soap.sforce.com/2006/04/metadata">
3    <types>
4        <members>hotelCard</members>
5        <name>UiWidgetBundle</name>
6    </types>
7    <version>67.0</version>
8</Package>

To delete a widget, you must deploy a destructiveChanges package to your org that lists the widgets to delete.

See Also

Salesforce CLI Commands 

Use the Salesforce CLI project commands to deploy, retrieve, and track the source files for the UiWidgetBundle metadata type in your Salesforce DX project. See project Commands in the Salesforce CLI Command Reference.

This example shows how to deploy a UiWidgetBundle called hotelCard to an org with alias my-org:

1sf project deploy start --metadata UiWidgetBundle:hotelCard --target-org my-org

Connect Your Widget to a Custom Lightning Type 

To render dynamic data in a widget, connect the widget to a custom Lightning type. In the custom Lightning type’s renderer.json file, reference the widget and map properties defined by the custom Lightning type to attributes defined in the widget’s schema.json file.

The widget bundle’s composition file, widget configuration file, and schema file don’t change based on the supported Salesforce application or third-party platform where the widget renders.

Don’t use the Lightning Types page in Setup to create or modify custom Lightning types that reference widgets. Define and update these custom Lightning types in your Salesforce DX project, and deploy the LightningTypeBundle metadata by using Salesforce CLI or Metadata API.

Note

Choose the Custom Lightning Type Configuration 

Choose the custom Lightning type configuration based on the scenario in which the widget renders.

Rendering ScenarioCustom Lightning Type ConfigurationWidget Attribute Mapping
Agentforce action output rendered in Lightning ExperienceOne Apex-based custom Lightning typeDirect mapping
MCP server tool output rendered in an MCP channelTwo object-based custom Lightning types, one in the MCP result wrapper role and one in the MCP payload roleNested mapping through outputValues

For Agentforce action output, the Apex-based custom Lightning type references the Apex class that defines the structured output’s schema. The custom Lightning type’s renderer.json file maps properties from that schema directly to attributes defined in the widget’s schema.json file.

For MCP server tool output from invocable actions, primarily Apex invocable actions exposed as MCP tools, create two object-based custom Lightning types. This configuration currently applies only to invocable actions exposed as MCP tools. Follow this fixed structure:

  • The MCP result wrapper custom Lightning type represents the complete result returned by the MCP server tool, including actionName, isSuccess, and outputValues.
  • The MCP payload custom Lightning type defines the structure referenced by the MCP result wrapper custom Lightning type’s outputValues property.

The terms MCP result wrapper custom Lightning type and MCP payload custom Lightning type describe roles in this configuration. They aren’t categories of custom Lightning types. Both are object-based custom Lightning types.

The outputValues property in the MCP result wrapper custom Lightning type references the MCP payload custom Lightning type. The MCP result wrapper custom Lightning type’s renderer.json file maps properties under outputValues to widget attributes.

For general information about creating custom Lightning types, see Apex-Based Custom Lightning Types and Object-Based Custom Lightning Types.

Reference the Widget 

Reference your widget in the Lightning type’s renderer.json file by using the componentOverrides syntax. The definition field uses this format: @widget/{namespace}/{widgetName}

  • Local Org (@widget/c/hotelCard): References a widget named hotelCard in your org’s namespace.

This example shows a basic widget reference:

1{
2  "renderer": {
3    "componentOverrides": {
4      "$": {
5        "definition": "@widget/c/hotelCard"
6      }
7    }
8  }
9}

For Agentforce action output, add renderer.json to the Apex-based custom Lightning type.

For MCP server tool output, add renderer.json to the MCP result wrapper custom Lightning type.

For widget references, create a renderer.json file at the root level (parallel to schema.json) in the Lightning type folder, without channel-specific subfolders.

Referencing widgets in editor.json isn’t currently supported. When you reference a widget in a Lightning type, create a renderer.json file at the root level (parallel to schema.json). Don’t use channel-specific subfolders. This structure differs from Lightning Web Component (LWC) overrides, which use channel-specific subfolders (such as lightningDesktopGenAi/). See Lightning Type Structure for Widgets (Beta).

Note

Widget Attribute Mapping 

Attribute mapping connects the data properties represented by a custom Lightning type to attributes defined in the widget’s schema.json file. Define the mapping in the attributes object of the custom Lightning type’s renderer.json file.

In the renderer’s attributes object, each entry maps a custom Lightning type property to a widget attribute.

  • The key is an attribute defined in the widget’s schema.json file.
  • The value references a property or nested property represented by the custom Lightning type. Use an expression such as {!$attrs.propertyName} for a direct property or {!$attrs.parentProperty.propertyName} for a nested property.

Attribute mapping requires a schema.json file in your widget to define the attributes. Widgets without a schema.json file can only display static content.

Note

The property reference depends on the rendering scenario:

  • Agentforce action output uses direct property references.
  • MCP server tool output uses nested property references through outputValues.

Map Agentforce Action Output 

For Agentforce action output rendered in Lightning Experience, map properties represented by the Apex-based custom Lightning type directly to widget attributes.

This example shows attribute mapping in a renderer.json file that connects properties represented by the custom Lightning type to the hotelCard widget.

1{
2  "renderer": {
3    "componentOverrides": {
4      "$": {
5        "definition": "@widget/c/hotelCard",
6        "attributes": {
7          "hotelId": "{!$attrs.hotelId}",
8          "name": "{!$attrs.name}",
9          "city": "{!$attrs.city}",
10          "checkInTime": "{!$attrs.checkInTime}",
11          "checkOutTime": "{!$attrs.checkOutTime}",
12          "pricePerNight": "{!$attrs.pricePerNight}",
13          "rating": "{!$attrs.rating}"
14        }
15      }
16    }
17  }
18}

In this example:

  • The keys (hotelId, name, city, checkInTime, checkOutTime, pricePerNight, and rating) are attributes defined in the widget’s schema.json file.
  • The values ({!$attrs.hotelId}, {!$attrs.name}, {!$attrs.city}, {!$attrs.checkInTime}, {!$attrs.checkOutTime}, {!$attrs.pricePerNight}, and {!$attrs.rating}) directly reference properties represented by the Apex-based custom Lightning type.

For information about creating the custom Lightning type, see Apex-Based Custom Lightning Types.

Map MCP Server Tool Output 

For a custom MCP server tool rendered in ChatGPT, Claude, or Slackbot, the MCP result wrapper custom Lightning type defines the outputValues property. The lightning:type value of outputValues references the MCP payload custom Lightning type.

In the MCP result wrapper custom Lightning type’s renderer.json file, use nested property references through outputValues to map MCP server tool output to widget attributes.

This example maps hotel information represented by an MCP result wrapper custom Lightning type to the hotelCard widget.

1{
2  "renderer": {
3    "componentOverrides": {
4      "$": {
5        "definition": "@widget/c/hotelCard",
6        "attributes": {
7          "hotelId": "{!$attrs.outputValues.hotelInfo.hotelId}",
8          "name": "{!$attrs.outputValues.hotelInfo.name}",
9          "city": "{!$attrs.outputValues.hotelInfo.city}",
10          "checkInTime": "{!$attrs.outputValues.hotelInfo.checkInTime}",
11          "checkOutTime": "{!$attrs.outputValues.hotelInfo.checkOutTime}",
12          "pricePerNight": "{!$attrs.outputValues.hotelInfo.pricePerNight}",
13          "rating": "{!$attrs.outputValues.hotelInfo.rating}"
14        }
15      }
16    }
17  }
18}

In this example:

  • The keys (hotelId, name, city, checkInTime, checkOutTime, pricePerNight, and rating) are attributes defined in the widget’s schema.json file.
  • $attrs.outputValues references the outputValues property in the MCP result wrapper custom Lightning type. The lightning:type value of this property references the MCP payload custom Lightning type.
  • hotelInfo is a property defined in the MCP payload custom Lightning type. Its lightning:type value references the HotelSearchResult.Hotel Apex class.
  • The final segment of each property reference, such as hotelId, name, or rating, references a field in the HotelSearchResult.Hotel Apex class.

The widget schema remains a flat attribute contract. Don’t reproduce the outputValues structure in the widget’s schema.json file. Only the MCP result wrapper custom Lightning type’s renderer.json file references the nested MCP server tool result structure.

For the required custom Lightning type configuration and MCP-specific schema requirements, see Configure Custom Lightning Types for an MCP Server Tool.

See Also

Configure Custom Lightning Types for an MCP Server Tool 

To render MCP server tool output in a widget, create two object-based custom Lightning types: an MCP result wrapper custom Lightning type and an MCP payload custom Lightning type. These terms describe the roles of the custom Lightning types in this configuration, not categories of custom Lightning types.

For general information about object-based custom Lightning types, see Object-Based Custom Lightning Types.

Understand the Custom Lightning Type Roles 

A custom MCP server tool returns a result containing actionName, isSuccess, and outputValues. At runtime, the outputValues property contains the action-specific payload.

Because a custom Lightning type can’t reference itself, use a separate MCP payload custom Lightning type to define the structure of the payload. The lightning:type value of outputValues references this custom Lightning type.

The MCP result wrapper custom Lightning type represents the complete MCP server tool result. Its outputValues property references the MCP payload custom Lightning type.

Define the MCP Payload Custom Lightning Type 

The lightning:type value of the MCP result wrapper custom Lightning type’s outputValues property references the MCP payload custom Lightning type. The MCP payload custom Lightning type defines the structure of the action-specific payload. Its properties can use Apex class type references to describe values returned by the Apex invocable action.

This example defines a hotelInfo property whose lightning:type value references the Hotel inner class in the HotelSearchResult Apex class.

1{
2  "title": "Hotel Info Output Values",
3  "description": "Payload fields from the HotelSearchResult response",
4  "type": "object",
5  "lightning:type": "lightning__objectType",
6  "lightning:tags": ["mcp"],
7  "unevaluatedProperties": false,
8  "properties": {
9    "hotelInfo": {
10      "title": "Hotel Info",
11      "lightning:type": "@apexClassType/c__HotelSearchResult$Hotel"
12    }
13  }
14}

For an MCP payload custom Lightning type:

  • Set type to object.
  • Set lightning:type to lightning__objectType.
  • Include mcp in lightning:tags.
  • Set unevaluatedProperties to false.
  • Define the properties in the payload.

In this example, the lightning:type value of hotelInfo references the Hotel inner class in the HotelSearchResult Apex class.

Define the MCP Result Wrapper Custom Lightning Type 

The MCP result wrapper custom Lightning type represents the complete result returned by the MCP server tool.

Define these properties:

  • actionName: The name of the invoked action.
  • isSuccess: Indicates whether the action completed successfully.
  • outputValues: The action-specific payload. Its lightning:type value references the MCP payload custom Lightning type.
1{
2  "title": "Hotel Info Result",
3  "description": "Invocable-action result envelope for the HotelSearchResult MCP tool",
4  "type": "object",
5  "lightning:type": "lightning__objectType",
6  "lightning:tags": ["mcp"],
7  "unevaluatedProperties": false,
8  "properties": {
9    "actionName": { "title": "Action Name", "lightning:type": "lightning__textType" },
10    "isSuccess": { "title": "Is Success", "lightning:type": "lightning__booleanType" },
11    "outputValues": { "title": "Output Values", "lightning:type": "c__hotelInfoOutputValues" }
12  }
13}

In this example, the lightning:type value of outputValues references hotelInfoOutputValues, the MCP payload custom Lightning type.

Add the Renderer 

Add renderer.json to the root of the MCP result wrapper custom Lightning type bundle, parallel to its schema.json file.

Map nested properties under outputValues to the flat attributes defined in the widget’s schema.json file.

This example maps hotel information under outputValues.hotelInfo to attributes defined in the hotelCard widget’s schema.

1{
2  "renderer": {
3    "componentOverrides": {
4      "$": {
5        "definition": "@widget/c/hotelCard",
6        "attributes": {
7          "hotelId": "{!$attrs.outputValues.hotelInfo.hotelId}",
8          "name": "{!$attrs.outputValues.hotelInfo.name}",
9          "city": "{!$attrs.outputValues.hotelInfo.city}",
10          "checkInTime": "{!$attrs.outputValues.hotelInfo.checkInTime}",
11          "checkOutTime": "{!$attrs.outputValues.hotelInfo.checkOutTime}",
12          "pricePerNight": "{!$attrs.outputValues.hotelInfo.pricePerNight}",
13          "rating": "{!$attrs.outputValues.hotelInfo.rating}"
14        }
15      }
16    }
17  }
18}

In this example:

  • The keys (hotelId, name, city, checkInTime, checkOutTime, pricePerNight, and rating) are attributes defined in the hotelCard widget’s schema.json file.
  • outputValues is a property in the MCP result wrapper custom Lightning type.
  • hotelInfo is a property in the MCP payload custom Lightning type.
  • The final segment of each expression references a property in the HotelSearchResult.Hotel Apex class.

Don’t add renderer.json to the MCP payload custom Lightning type. The renderer applies to the complete MCP server tool result represented by the MCP result wrapper custom Lightning type.

Understand the File Structure 

This example uses an MCP server tool for hotel information and a widget named hotelCard.

1myProject/
2  force-app/
3    main/
4      default/
5        uiWidgets/
6          hotelCard/
7            hotelCard.json
8            hotelCard.uiwidget-meta.xml
9            schema.json
10        lightningTypes/
11          hotelInfoResult/
12            schema.json
13            renderer.json
14          hotelInfoOutputValues/
15            schema.json

In this structure:

  • hotelInfoOutputValues is the bundle for the MCP payload custom Lightning type.
  • hotelInfoResult is the bundle for the MCP result wrapper custom Lightning type.
  • The hotelInfoResult bundle contains renderer.json.
  • hotelCard is the widget bundle.

See Also

Understand Namespace Prefixes for Widget References 

When you reference a widget from a Lightning type, use the correct namespace prefix.

For widgets created in your org

Use the c namespace prefix. This rule applies even if your org has its own namespace. For example, if your org’s namespace is myOrgNamespace and you create a widget named hotelCard, refer to it as c/hotelCard.

This renderer.json file uses the c namespace to reference a widget.

1{
2  "renderer": {
3    "componentOverrides": {
4      "$": {
5        "definition": "@widget/c/hotelCard"
6      }
7    }
8  }
9}

See Also

Beta Feature

Headless Experience Layer is a pilot or beta service that is subject to the Beta Services Terms at Ageements - Salesforce.com or a written Unified Pilot Agreement if executed by Customer, and applicable terms in the Product Terms Directory. Use of this pilot or beta service is at the Customer's sole discretion.