# Custom Lightning Types on Mobile with the Agentforce Mobile SDK

Use Custom Lightning Types (CLTs) to control how structured agent action data appears in Agentforce on mobile. In apps that integrate the Agentforce Mobile SDK, you can route CLT output and input through native UI (SwiftUI or Jetpack Compose) by implementing a view provider and using the `AFMobileCustom/` prefix in your CLT metadata.

> For LWC-based CLT overrides in the Salesforce mobile app, see [Custom Lightning Types in the Salesforce Mobile App](https://help.salesforce.com/s/articleView?id=xcloud.salesforce_app_clt_mobile.htm\&type=5) in Salesforce Help.

## Configure CLT metadata for mobile

Agentforce CLT UI configuration is organized by channel, similar to desktop. For Agentforce on mobile, add a `lightningMobileGenAi` folder next to your existing channel folders (for example `lightningDesktopGenAi`). You can configure desktop and mobile independently.

Output (renderer) — example layout for a type named `flightResponse`:

```
+--lightningTypes
    +--flightResponse
        +--schema.json
        +--lightningDesktopGenAi
            +--renderer.json
        +--lightningMobileGenAi
            +--renderer.json
```

Input (editor) — example layout for a type named `flightFilter`:

```
+--lightningTypes
    +--flightFilter
        +--schema.json
        +--lightningDesktopGenAi
            +--editor.json
        +--lightningMobileGenAi
            +--editor.json
```

### Renderer override for native mobile (output)

In `lightningMobileGenAi/renderer.json`, use the top-level `$` override and set `definition` to an `AFMobileCustom/…` identifier that your app handles.

```json
{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "AFMobileCustom/FlightDetails"
      }
    }
  }
}
```

### Map schema fields to native properties

If your native view expects different property names than the schema, use `attributes` (same idea as desktop channel overrides):

```json
{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "AFMobileCustom/FlightDetails",
        "attributes": {
          "flightCost": "{!$attrs.price}",
          "discountPercentage": "{!$attrs.discountPercentage}"
        }
      }
    }
  }
}
```

### Editor override for native mobile (input)

In `lightningMobileGenAi/editor.json`:

```json
{
  "editor": {
    "componentOverrides": {
      "$": {
        "definition": "AFMobileCustom/FlightFilter"
      }
    }
  }
}
```

The editor is shown when the type is used as input in the mobile conversation; implementation uses the same view provider pattern as renderers.

## Implement native UI on iOS

1. Conform to `AgentforceViewProviding`.
2. Return `true` from `canHandle(type:)` for each `AFMobileCustom/…` definition you support.
3. Return the appropriate SwiftUI view from `view(for:data:)`.

```sfdocs-code {"lang":"swift", "title": "Example view provider (SwiftUI)"}
import SwiftUI
import AgentforceSDK

class FlightDetailsViewProvider: AgentforceViewProviding {
    func canHandle(type: String) -> Bool {
        type == "AFMobileCustom/FlightDetails"
    }

    @MainActor
    func view(for type: String, data: [String: Any]) -> AnyView {
        switch type {
        case "AFMobileCustom/FlightDetails":
            return AnyView(FlightDetailsView(data: data))
        default:
            return AnyView(EmptyView())
        }
    }
}
```

Register the provider when you create `AgentforceClient`. Pass `viewProvider` along with `credentialProvider`, `mode`, and other required parameters for your agent type (employee, service, or full configuration), as described in [Integrate Agentforce SDK](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-ios-integration.md).

```sfdocs-code {"lang":"swift", "title": "Register the view provider"}
let client = AgentforceClient(
    credentialProvider: credentialProvider,
    mode: .employeeAgent(config),
    viewProvider: FlightDetailsViewProvider(),
    themeManager: AgentforceDefaultThemeManager()
)
```

For more on client setup, see [iOS configuration](/docs/ai/agentforce-mobile-sdk/references/agentforce-mobile-sdk/configuration-overview-ios.md) in the Agentforce Mobile SDK reference (including AgentforceClient and AgentforceConfiguration).

## Implement native UI on Android

1. Implement `AgentforceViewProvider`.
2. Return `true` from `canHandle(definition:)` for each `AFMobileCustom/…` definition.
3. In `GetView`, read payload data (for example from `AgentforceComponent` / `view.properties`) and compose your UI.

```sfdocs-code {"lang":"kotlin", "title": "Example view provider (Jetpack Compose)"}
class FlightDetailsViewProvider : AgentforceViewProvider {
    override fun canHandle(definition: String): Boolean {
        return definition == "AFMobileCustom/FlightDetails"
    }

    @Composable
    override fun GetView(modifier: Modifier, view: AgentforceComponent) {
        val flightData = view.properties["value"]
        FlightDetailsCard(modifier = modifier, data = flightData)
    }
}
```

Register the provider on `AgentforceConfiguration`:

```sfdocs-code {"lang":"kotlin", "title": "Register the view provider"}
val config = AgentforceConfiguration.builder(
    authCredentialProvider = /* your provider */
)
    .setViewProvider(FlightDetailsViewProvider())
    // ... other configuration
    .build()
```

See [Integrate Agentforce SDK](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-android-integration.md) and [Android configuration](/docs/ai/agentforce-mobile-sdk/references/agentforce-mobile-sdk/configuration-overview-android.md) in the Agentforce Mobile SDK reference for `setViewProvider` and related builder options.

## Handle multiple definitions in one provider

You can implement `canHandle` / `GetView` (or `view(for:data:)` on iOS) for several `AFMobileCustom/…` strings in a single class.

iOS (SwiftUI)

```sfdocs-code {"lang":"swift", "title": "Single provider for renderer and editor"}
class MyAppViewProvider: AgentforceViewProviding {
    func canHandle(type: String) -> Bool {
        type == "AFMobileCustom/FlightDetails" ||
        type == "AFMobileCustom/FlightFilter"
    }

    @MainActor
    func view(for type: String, data: [String: Any]) -> AnyView {
        switch type {
        case "AFMobileCustom/FlightDetails":
            return AnyView(FlightDetailsView(data: data))
        case "AFMobileCustom/FlightFilter":
            return AnyView(FlightFilterView(data: data))
        default:
            return AnyView(EmptyView())
        }
    }
}
```

Android (Jetpack Compose)

```sfdocs-code {"lang":"kotlin", "title": "Single provider for renderer and editor"}
class MyAppViewProvider : AgentforceViewProvider {
    override fun canHandle(definition: String): Boolean {
        return definition in listOf(
            "AFMobileCustom/FlightDetails",
            "AFMobileCustom/FlightFilter"
        )
    }

    @Composable
    override fun GetView(modifier: Modifier, view: AgentforceComponent) {
        when (view.definition) {
            "AFMobileCustom/FlightDetails" ->
                FlightDetailsCard(modifier = modifier, data = view.properties["value"])
            "AFMobileCustom/FlightFilter" ->
                FlightFilterEditor(modifier = modifier, data = view.properties["value"])
            else -> { /* no-op */ }
        }
    }
}
```

## React Native apps

Custom Lightning Types are supported in apps that use the Agentforce React Native SDK when you render them with native-style overrides: CLT metadata that uses the `AFMobileCustom/…` prefix, implemented through the native iOS and Android SDKs behind the bridge.

1. Enable `enableCustomViewProvider` in feature flags.
2. Register a View Provider delegate and map each `AFMobileCustom/…` definition string to a React Native component (registered with `AppRegistry`), the same way you map other SDK component definitions. See [Delegates](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-react-native-delegates.md).

> **Tip:** Before mapping a definition in your `componentMap`, log the actual `definition` prop that the native SDK sends to the rendered component. The native `canHandle()` lookup is an exact string match — any mismatch (casing, extra whitespace, typo) silently renders nothing.

Not supported in React Native today — using Lightning web components (LWC) as the CLT renderer or editor inside the bridge, and using React web components inside CLT. Those UI paths are separate from the native-override flow above. For details and caveats, see [React Native SDK Limitations](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-react-native-limitations.md#custom-lightning-types-lwc-and-react-web-ui).

## See also

- [Message Response Formatting](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-message-formatting.md) (Markdown responses and HTML in the `lightning__richText` component)
- [Custom Lightning Types in the Salesforce Mobile App](https://help.salesforce.com/s/articleView?id=xcloud.salesforce_app_clt_mobile.htm\&type=5) (LWC overrides in the Salesforce mobile app)
- [Customize the Agent Action UI with Lightning Types](/docs/ai/agentforce/guide/lightning-types.md)
- [Customizing User Interface Using Custom Lightning Types with Top-Level Editor and Top-Level Renderer Overrides](/docs/ai/agentforce/guide/lightning-types-example-full-editor-renderer.md)
- [Integrate Agentforce SDK (iOS)](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-ios-integration.md)
- [Integrate Agentforce SDK (Android)](/docs/ai/agentforce-mobile-sdk/guide/agentforce-mobile-sdk-android-integration.md)
