Example: Mapping a Hotel Card Widget for Agentforce

This example shows how Agentforce action output rendered in Lightning Experience flows through an Apex-based custom Lightning type named hotelInfo to the hotelCard widget.

This example uses two schema files that serve different purposes:

  • The Lightning type schema describes the data shape—the data structure of hotel information returned by your data source
  • The widget schema describes the UI shape—the attribute contract that defines what the card component needs to render

You map the data shape to the UI shape by using the renderer file.

Before You Begin 

Download these sample data files.

Project Directory Workspace Structure 

This layout shows where the custom UiWidgetBundle and related LightningTypeBundle component files belong in your Salesforce DX project.

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          hotelInfo/
12            schema.json
13            renderer.json

In this directory structure, renderer.json sits directly parallel to schema.json. For widget references, create this file at the root level without using channel-specific subfolders. See Connect Your Widget to a Custom Lightning Type.

Note

See Also

Declare the Widget Attribute Contract 

The widget schema file (uiWidgets/hotelCard/schema.json) defines the attributes that your widget accepts. These attributes become available as {!$attrs.attributeName} bindings in your widget’s composition file and as mapping targets in the Lightning type’s renderer.json file.

1{
2  "title": "Get Hotel Details Widget",
3  "description": "Displays hotel details including name, city, check-in and check-out times, nightly price, and rating.",
4  "type": "object",
5  "properties": {
6    "attributes": {
7      "lightning:type": "lightning__objectType",
8      "properties": {
9        "hotelId": {
10          "title": "Hotel ID",
11          "description": "Unique hotel identifier (e.g. HTL-101)",
12          "lightning:type": "lightning__textType"
13        },
14        "name": {
15          "title": "Name",
16          "description": "Hotel name",
17          "lightning:type": "lightning__textType"
18        },
19        "city": {
20          "title": "City",
21          "description": "City where the hotel is located",
22          "lightning:type": "lightning__textType"
23        },
24        "checkInTime": {
25          "title": "Check-In Time",
26          "description": "Standard check-in time",
27          "lightning:type": "lightning__textType"
28        },
29        "checkOutTime": {
30          "title": "Check-Out Time",
31          "description": "Standard check-out time",
32          "lightning:type": "lightning__textType"
33        },
34        "pricePerNight": {
35          "title": "Price Per Night",
36          "description": "Nightly price in USD",
37          "lightning:type": "lightning__numberType"
38        },
39        "rating": {
40          "title": "Rating",
41          "description": "Guest rating out of 5",
42          "lightning:type": "lightning__numberType"
43        }
44      }
45    }
46  }
47}

See Also

Build the Widget Composition 

The composition file (uiWidgets/hotelCard/hotelCard.json) defines the widget’s visual structure and binds the widget’s schema attributes to UI components by using {!$attrs.attributeName} expressions.

1{
2  "type": "lightning__agentforceWidget",
3  "contentBody": {
4    "widgetBody": {
5      "definition": "tile/widget",
6      "children": [
7        {
8          "definition": "tile/column",
9          "attributes": {
10            "gap": "md"
11          },
12          "children": [
13            {
14              "definition": "tile/container",
15              "children": [
16                {
17                  "definition": "tile/text",
18                  "attributes": {
19                    "text": "{!$attrs.name}",
20                    "variant": "h2"
21                  }
22                },
23                {
24                  "definition": "tile/row",
25                  "attributes": {
26                    "gap": "sm"
27                  },
28                  "children": [
29                    {
30                      "definition": "tile/text",
31                      "attributes": {
32                        "text": "{!$attrs.city}",
33                        "variant": "caption",
34                        "color": "muted"
35                      }
36                    },
37                    {
38                      "definition": "tile/text",
39                      "attributes": {
40                        "text": "•",
41                        "variant": "caption",
42                        "color": "muted"
43                      }
44                    },
45                    {
46                      "definition": "tile/text",
47                      "attributes": {
48                        "text": "{!$attrs.hotelId}",
49                        "variant": "caption",
50                        "color": "muted"
51                      }
52                    }
53                  ]
54                }
55              ]
56            },
57            {
58              "definition": "tile/container",
59              "children": [
60                {
61                  "definition": "tile/column",
62                  "attributes": {
63                    "gap": "sm"
64                  },
65                  "children": [
66                    {
67                      "definition": "tile/row",
68                      "attributes": {
69                        "gap": "sm"
70                      },
71                      "children": [
72                        {
73                          "definition": "tile/text",
74                          "attributes": {
75                            "text": "Check-In",
76                            "variant": "body",
77                            "weight": "semibold"
78                          }
79                        },
80                        {
81                          "definition": "tile/text",
82                          "attributes": {
83                            "text": "{!$attrs.checkInTime}",
84                            "variant": "body"
85                          }
86                        }
87                      ]
88                    },
89                    {
90                      "definition": "tile/row",
91                      "attributes": {
92                        "gap": "sm"
93                      },
94                      "children": [
95                        {
96                          "definition": "tile/text",
97                          "attributes": {
98                            "text": "Check-Out",
99                            "variant": "body",
100                            "weight": "semibold"
101                          }
102                        },
103                        {
104                          "definition": "tile/text",
105                          "attributes": {
106                            "text": "{!$attrs.checkOutTime}",
107                            "variant": "body"
108                          }
109                        }
110                      ]
111                    },
112                    {
113                      "definition": "tile/row",
114                      "attributes": {
115                        "gap": "sm"
116                      },
117                      "children": [
118                        {
119                          "definition": "tile/text",
120                          "attributes": {
121                            "text": "Price / Night (USD)",
122                            "variant": "body",
123                            "weight": "semibold"
124                          }
125                        },
126                        {
127                          "definition": "tile/text",
128                          "attributes": {
129                            "text": "{!$attrs.pricePerNight}",
130                            "variant": "body"
131                          }
132                        }
133                      ]
134                    },
135                    {
136                      "definition": "tile/row",
137                      "attributes": {
138                        "gap": "sm"
139                      },
140                      "children": [
141                        {
142                          "definition": "tile/text",
143                          "attributes": {
144                            "text": "Rating",
145                            "variant": "body",
146                            "weight": "semibold"
147                          }
148                        },
149                        {
150                          "definition": "tile/text",
151                          "attributes": {
152                            "text": "{!$attrs.rating}",
153                            "variant": "body"
154                          }
155                        }
156                      ]
157                    }
158                  ]
159                }
160              ]
161            }
162          ]
163        }
164      ]
165    }
166  }
167}

See Also

Define Widget Configuration 

The configuration file (uiWidgets/hotelCard/hotelCard.uiwidget-meta.xml) declares the widget bundle type required for deployment.

1<?xml version="1.0" encoding="UTF-8"?>
2<UiWidgetBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3    <masterLabel>Hotel Card</masterLabel>
4    <description>Displays hotel details including name, city, check-in and check-out times, nightly price, and rating.</description>
5    <widgetType>JSON</widgetType>
6</UiWidgetBundle>

See Also

Define the Lightning Type Schema 

The custom Lightning type schema file (lightningTypes/hotelInfo/schema.json) defines the data structure. This schema establishes the properties available for mapping to your widget.

1{
2  "title": "Hotel Information",
3  "description": "Structured hotel data returned by the booking agent",
4  "lightning:type": "@apexClassType/c__HotelSearchResult$Hotel"
5}

See Also

Map Lightning Type Data to the Widget 

The renderer file (lightningTypes/hotelInfo/renderer.json) maps the Lightning type’s data properties to the widget’s attributes.

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}

For detailed information about widget references and attribute mapping, see Connect Your Widget to a Custom Lightning Type.

Note

See Also

Package Deployment Management 

To deploy both the Lightning type and Widget together, include them in a single package.xml manifest.

This example shows a manifest that includes both the hotelCard widget and hotelInfo Lightning type.

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    <types>
8        <members>hotelInfo</members>
9        <name>LightningTypeBundle</name>
10    </types>
11    <version>67.0</version>
12</Package>

Deployment Order 

When you deploy a Lightning type that references a widget, deploy the widget first or include it in the same package. Metadata API resolves deployment ordering automatically within a single package.

If you deploy the components independently across separate deployments, maintain this sequential order:

  1. Deploy the UiWidgetBundle package to your target org first.
  2. Deploy the LightningTypeBundle package to your target org second.

After Deployment 

After you deploy the widget and the custom Lightning type, the widget is ready to render Agentforce action output in Lightning Experience.

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.