SfSkills Domains Docs GitHub

Home / agentforce / agentforce-custom-lightning-types

agentforce-custom-lightning-types

Use when overriding the default UI of an Agentforce agent action's Apex input or output with your own Lightning web components via a LightningTypeBundle (schema.json + editor.json/renderer.json, lightning__AgentforceInput / lightning__AgentforceOutput targets, the @apexClassType binding). NOT for standard agent actions with primitive inputs/outputs (only Apex-class input/output can be overridden), NOT for Flow screen custom property editors (use flow/flow-custom-property-editors), and NOT for general LWC-in-Lightning-page work (use lwc/* skills).

Open the skill folder on GitHub


Agentforce Custom Lightning Types

This skill activates when a practitioner wants to replace the generic UI an Agentforce agent shows for an action's input or output with a purpose-built Lightning web component. The mechanism is a LightningTypeBundle — a deployable metadata bundle of a JSON schema plus channel-scoped editor/renderer overrides — and it only applies to agent actions whose input or output is an Apex class.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

The bundle: schema, editor, renderer

A custom Lightning type is built from three artifacts:

Directory layout

The bundle lives under lightningTypes/, with channel-scoped editor/renderer files nested in a channel folder:

force-app/main/default/
├── lightningTypes/
│   ├── flightResponse/
│   │   ├── schema.json
│   │   └── lightningDesktopGenAi/
│   │       └── renderer.json
│   └── flightFilter/
│       ├── schema.json
│       └── lightningDesktopGenAi/
│           └── editor.json
└── lwc/
    ├── flightDetails/        (renderer LWC, target lightning__AgentforceOutput)
    └── flightFilter/         (editor LWC,   target lightning__AgentforceInput)

The componentOverrides contract

Both editor.json and renderer.json map a schema scope to an LWC by name. The key "$" means a top-level (whole-type) override; a property name keys a single-field override; "collection" (renderer only) overrides how a list of the type renders.

// renderer.json — replace the whole-type output UI with c/flightDetails
{ "renderer": { "componentOverrides": { "$": { "definition": "c/flightDetails" } } } }
// editor.json — replace the whole-type input UI with c/flightFilter
{ "editor": { "componentOverrides": { "$": { "definition": "c/flightFilter" } } } }

Use the c/ prefix for org metadata and the isv/ prefix for components delivered in a managed package.

Channels

The channel folder name scopes where the override applies:

Channel folderWhere the override applies
lightningDesktopGenAiAgentforce Employee agent in Lightning Experience (desktop)
lightningMobileGenAiEmployee agent on mobile; Service agent via Enhanced Chat v2 on mobile
enhancedWebChatAgentforce Service agent via Enhanced Chat v2
experienceBuilderExperience Builder (renderer.json is not supported here)

Common Patterns

Branded output card (renderer)

When to use: the agent returns a structured Apex result (an order, a flight, a case summary) and the default rendering shows it as raw fields or JSON.

How it works: annotate the output Apex class fields with @AuraEnabled; create a schema.json bound to it via @apexClassType/c__Flight; build a renderer LWC (lightning__AgentforceOutput) that reads the @api value payload; point renderer.json's "$" override at it; ship it in the channel folder for the surfaces you support.

Why not the alternative: stuffing presentation hints into the action's description or returning pre-formatted strings from Apex couples your data contract to one display and defeats the agent's ability to reason over typed fields.

Interactive input form (editor)

When to use: the action needs richer input than the default form — a date-range picker, dependent dropdowns, validation beyond the schema.

How it works: build an editor LWC (lightning__AgentforceInput) that exposes @api value and @api schema, and dispatches a valuechange event when the user edits, so the host binds the data in real time. Reference it from editor.json's "$" override.

Why not the alternative: the default editor can't enforce cross-field rules or custom widgets; pushing that logic into Apex validation only surfaces errors after submission.

Collection rendering

When to use: the action returns a list of the type (e.g. several hotels) and you want a consistent list/grid rather than per-item default cards.

How it works: add a "collection" key to the renderer's componentOverrides pointing at an LWC that receives the array, alongside (or instead of) the "$" single-item override.


Decision Guidance

SituationRecommended ApproachReason
Action input/output is a primitive (String/Integer)Refactor the invocable to use an Apex wrapper class firstOnly Apex-class input/output can be overridden
You only need to change how a result looksRenderer-only bundle (renderer.json)Output display is the renderer's job
You need a custom input widget or cross-field validationEditor bundle (editor.json) with a valuechange-dispatching LWCThe editor is the design-time/input surface
Same type, multiple surfaces (desktop + mobile + chat)One bundle, multiple channel foldersOverrides are channel-scoped; missing folder = default UI
Component ships in a managed packageUse the isv/ definition prefixc/ is org metadata only
Need custom UI in Experience Builder outputNot available via renderer.jsonrenderer.json is unsupported in experienceBuilder

  1. Confirm eligibility — verify the agent action's input or output parameter is an Apex class (refactor to a wrapper class if it isn't).
  2. Prepare the Apex type — annotate every field to expose with @AuraEnabled; mark the class global and top-level; add @JsonAccess(serializable='always' deserializable='always').
  3. Author schema.json — set title/description and bind to the Apex class with "lightning:type": "@apexClassType/c__ClassName". Do not duplicate the field list; the platform projects it.
  4. Build the LWC(s) — a renderer LWC (lightning__AgentforceOutput) for output and/or an editor LWC (lightning__AgentforceInput) for input, exposing @api value (and @api schema); editors dispatch valuechange.
  5. Wire the override — create the channel folder (e.g. lightningDesktopGenAi/) and add editor.json and/or renderer.json with a "$" (or "collection") override pointing at c/<lwc>.
  6. Deploy and verify — deploy the LightningTypeBundle and lwc at API 64.0+; open the agent on each targeted channel and confirm the custom UI renders (and falls back gracefully where no folder exists).

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Only Apex-class actions are overridable — a custom Lightning type silently has no effect on an action whose input/output is a primitive or a standard type. The fix is upstream in the Apex signature, not in the bundle.
  2. Channel-scoped fallback is silent — if you ship lightningDesktopGenAi/ but the agent also runs on mobile, the mobile surface quietly uses the default UI. There's no error; the override just doesn't appear.
  3. renderer.json is unsupported in experienceBuilder — placing one there does not customize the output; only editor overrides apply on that surface.

Output Artifacts

ArtifactDescription
lightningTypes/{type}/schema.jsonJSON-Schema type bound to the backing Apex class via @apexClassType/
lightningTypes/{type}/{channel}/editor.json / renderer.jsonChannel-scoped componentOverrides mapping a scope to an LWC
Editor / renderer LWC bundlelightning__AgentforceInput / lightning__AgentforceOutput component exposing @api value
package.xmlDeployment manifest with <name>LightningTypeBundle</name> at version 64.0+

Package files