Supercharge Agentforce with Salesforce LWC Data Tables: A Step-by-Step Guide

Unlock powerful data visualization within Agentforce Employee Agent by seamlessly integrating Salesforce Lightning Web Component (LWC) Data Tables. This comprehensive guide will walk you through building custom Salesforce Lightning types to display dynamic data, enhancing the user interface of your custom agent actions in Lightning Experience.
Are your Agentforce users craving a more intuitive and structured way to view data from custom actions? The default output might be functional, but imagine the impact of presenting information in a clear, sortable, and interactive data table directly within Agentforce. This is precisely what we’ll achieve by combining the power of Salesforce Apex, Lightning Web Components, and custom Lightning Types.
This blog post will delve into:
- Why use LWC Data Tables in Agentforce?
- Designing your Apex Invocable Method for Agentforce compatibility.
- Building a reusable Lightning Web Component (LWC) for data display.
- Configuring Custom Lightning Types for seamless integration.
- Putting it all together: A practical example with Product Search.
Ready to elevate your Agentforce experience? Let’s dive in!
Why Use LWC Data Tables in Agentforce Output?
The Agentforce Employee Agent in Lightning Experience is designed to streamline agent workflows. While custom agent actions provide immense flexibility, presenting complex data in a raw format can hinder efficiency. Here’s why LWC Data Tables are a game-changer for Agentforce output:
- Enhanced Readability: Data tables organize information into rows and columns, making it easy to scan and understand.
- Improved User Experience: Agents can quickly grasp the relevant details without sifting through unstructured text.
- Interactivity: LWC Data Tables offer built-in features like sorting and resizing columns, empowering agents to manipulate the data directly.
- Consistency: Maintain a consistent look and feel with the rest of your Lightning Experience UI.
- Scalability: Easily display varying amounts of data without compromising performance or layout.
By leveraging LWC Data Tables, you transform your Agentforce outputs from static text into dynamic, actionable insights, ultimately boosting agent productivity.
Step 1: Design Your Apex Invocable Method for Agentforce
The heart of our solution lies in an Apex InvocableMethod
that fetches the data. For Agentforce to consume this data effectively, we need to define specific @InvocableVariable
classes for both input and output. This ensures that Agentforce can correctly interpret the data structure.
Let’s look at our SearchProductController
Apex class:
Sample Apex Code:
public with sharing class SearchProductController {
@InvocableMethod(
label='Search Products'
description='Finds products based on the given criteria'
)
public static List < ProductResponse > searchProducts( List < ProductRequest > req ) {
List < ProductWrapper > listProductWraps = new List < ProductWrapper > ();
List < ProductResponse> productResponses = new List< ProductResponse >();
ProductRequest request = req[ 0 ];
Integer intCount = request.Count;
String productName = request.Name;
System.debug( 'Count: ' + intCount );
System.debug( 'Product Name: ' + productName );
List < Product2 > listProducts = [
SELECT Id, Name, ProductCode
FROM Product2
WHERE Name LIKE :('%' + productName + '%')
ORDER BY Name ASC
LIMIT :intCount
];
for ( Product2 objProduct : listProducts ) {
System.debug('Product: ' + objProduct);
ProductWrapper objWrap = new ProductWrapper(
objProduct.Name,
objProduct.ProductCode
);
System.debug( 'Product Object: ' + objWrap );
listProductWraps.add( objWrap );
}
ProductResponse productResponse = new ProductResponse();
productResponse.availableProducts = listProductWraps;
System.debug( 'Product Responses: ' + productResponses );
productResponses.add(productResponse);
System.debug( 'Product Responses After Adding: ' + productResponses );
return productResponses;
}
public class ProductRequest {
@InvocableVariable
public String Name;
@InvocableVariable
public Integer Count;
}
public class ProductResponse {
@InvocableVariable
public List < ProductWrapper > availableProducts;
}
public class ProductWrapper {
@InvocableVariable
public String productName;
@InvocableVariable
public String productCode;
public ProductWrapper(
String productName,
String productCode
) {
this.productName = productName;
this.productCode = productCode;
}
}
}
Key takeaways from the Apex class:
@InvocableMethod
: This annotation makes thesearchProducts
method callable from external services, including Agentforce actions.ProductRequest
: Defines the input parameters for our product search (e.g.,Name
,Count
). Notice the@InvocableVariable
annotation, which is crucial for Agentforce to pass values.ProductResponse
: This class encapsulates the output of our invocable method. It contains a list ofProductWrapper
objects.ProductWrapper
: This inner class defines the structure of each product record returned. Each property (productName
,productCode
) is marked with@InvocableVariable
, making it accessible to our Lightning Type.
Step 2: Build Your Lightning Web Component (LWC) Data Table
Next, we’ll create an LWC that receives the data from our Apex method and displays it in a lightning-datatable
.
HTML (productSearchDetails.html
):
HTML
<template>
<lightning-card title="Products" icon-name="standard:product">
<lightning-datatable
data={products}
columns={columns}
hide-checkbox-column
key-field="productCode">
</lightning-datatable>
</lightning-card>
</template>
JavaScript (productSearchDetails.js
):
JavaScript
import { LightningElement, api } from 'lwc';
export default class ProductSearchDetails extends LightningElement {
@api value; // This property will receive the data from Agentforce
columns = [
{ label: 'Product Name', fieldName: 'productName' },
{ label: 'Product Code', fieldName: 'productCode' }
];
get products() {
let tempProducts = [];
// Ensure 'value' exists and is an array before iterating
if (this.value && Array.isArray(this.value)) {
for ( const element of this.value ) {
console.log( element );
tempProducts.push( {
productName: element.productName,
productCode: element.productCode
} );
}
}
return tempProducts;
}
}
LWC Explanation:
@api value;
: This is the most critical part. The@api
decorator makes thevalue
property public and reactive. Agentforce will automatically pass the data structured according to ourProductWrapper
Apex class into thisvalue
property.columns
: Defines the columns for ourlightning-datatable
, specifying thelabel
(what’s displayed) andfieldName
(which property from ourProductWrapper
to map to).get products()
: This getter transforms the incomingvalue
data into a format suitable for thelightning-datatable
. It iterates through thevalue
(which will be a list ofProductWrapper
objects) and creates a new array of objects withproductName
andproductCode
properties. The added checkif (this.value && Array.isArray(this.value))
ensures robustness ifvalue
is not yet populated or is not an array.lightning-datatable
: The core component for displaying tabular data.data={products}
: Binds the data from ourproducts
getter.columns={columns}
: Binds the column definitions.hide-checkbox-column
: Removes the default checkbox column.key-field="productCode"
: Essential for performance and unique row identification in data tables.
XML Configuration (productSearchDetails.js-meta.xml
):
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>64.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Product Details</masterLabel>
<targets>
<target>lightning__AgentforceOutput</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AgentforceOutput">
<sourceType name="lightning__listType" itemTypeName="c__productSearchResponse"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
js-meta.xml
highlights:
<isExposed>true</isExposed>
: Makes the LWC available for use in Lightning App Builder and other contexts.<target>lightning__AgentforceOutput</target>
: This is crucial! It explicitly makes our LWC available as an output type within Agentforce actions.<targetConfig targets="lightning__AgentforceOutput">
: This section is specifically for Agentforce output.<sourceType name="lightning__listType" itemTypeName="c__productSearchResponse"/>
: This tells Agentforce that our LWC expects a list of items, and each item’s structure is defined by the custom Lightning Type namedc__productSearchResponse
. ThisitemTypeName
maps directly to theProductResponse
class in our Apex, specifically itsavailableProducts
list which containsProductWrapper
objects.
Step 3: Configure Custom Lightning Types for Seamless Integration
Custom Lightning Types bridge the gap between your Apex output and your LWC input. They define how Agentforce should interpret the data returned by your Apex invocable method and which LWC should render it.
You’ll create a folder within your force-app/main/default/customMetadata
directory (or similar, depending on your project structure) for your custom type. Let’s call it productSearchResponse.lightningType
. Inside this folder, you’ll have two files: schema.json
and renderer.json
.
schema.json
:
JSON
{
"title": "Product Search",
"description": "Product Search",
"lightning:type": "@apexClassType/c__SearchProductController$ProductWrapper"
}
schema.json
details:
"lightning:type": "@apexClassType/c__SearchProductController$ProductWrapper"
: This is the most important line. It explicitly tells Agentforce to map this custom type to theProductWrapper
inner class within ourSearchProductController
Apex class. This is because our LWCvalue
property is expecting a list ofProductWrapper
objects (which are nested withinProductResponse
).
renderer.json
:
JSON
{
"collection": {
"renderer": {
"componentOverrides": {
"$": {
"definition": "c/productSearchDetails"
}
}
}
}
}
renderer.json
details:
"collection"
: Indicates that this renderer is for a collection (list) of items."componentOverrides": { "$": { "definition": "c/productSearchDetails" } }
: This is where we tell Agentforce which LWC to use to render each item in the collection. The$
signifies that the entire collection should be rendered by ourproductSearchDetails
LWC.
Putting It All Together: Agentforce Configuration
Once you’ve deployed these components to your Salesforce org, you can configure your Agentforce action:
- Create an Agent Action: Navigate to Setup -> Agentforce -> Agent Actions (or search for it).
- Define Inputs: When configuring your Agent Action, you’ll specify the inputs that map to your
ProductRequest
Apex class (e.g., “Product Name”, “Count”). - Define Output: This is where the magic happens. For the output, select “Custom Salesforce Lightning Type”. You should then be able to choose your newly created
Product Search
type (which corresponds toc__productSearchResponse
).

When an agent executes this action, the Apex controller will fetch the product data, and Agentforce will use your custom Lightning Type to render the ProductWrapper
list beautifully within the lightning-datatable
of your productSearchDetails
LWC!
Conclusion
By following these steps, you can significantly enhance the user experience for your Agentforce Employee Agents. Displaying data in a well-structured and interactive lightning-datatable
directly within Agentforce output not only improves readability but also empowers agents with immediate, actionable insights. This powerful combination of Apex, LWC, and custom Lightning Types unlocks a new level of customization and efficiency for your Salesforce Agentforce implementation.

Reference Article:
https://developer.salesforce.com/docs/einstein/genai/guide/lightning-types-example-collection-renderer.html
Ready to transform your Agentforce outputs? Start implementing these techniques today!