Side Panel C3 Agent
It's possible to integrate a side panel to chat with an agent in any SDL page. Side panels can be collapsed or expanded, allowing AI interactions without interfering with existing UX. Follow the steps below to configure a side panel agent in your C3 Application.

Configuring a Side Panel Chat
In this example, we will assume we're configuring a Side Panel Chat for a "LightBulbs" application.
1. Prerequisites
To successfully configure the chat, ensure that:
- Your app must use the SDL Framework (at least at page level)
- Have a dependency on
genAiSearchv6.188 or greater
2. Create the JSON instance of the Side Panel Application State
File name: LightBulbsSidePanelAgentApplicationState.json:
{
"type": "GenAiUiFeedApplicationState",
"effectTriggers": [
{
"trigger": "LightBulbsSidePanelAgentApplicationState.POLL_RESULT",
"payloadStrategy": "MERGE",
"effectType": "GenAiUiEpicPollFeedQuery",
"payload": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState"
}
},
{
"trigger": "LightBulbsSidePanelAgentApplicationState.LOAD_RESULT_RESOURCES",
"payloadStrategy": "MERGE",
"effectType": "GenAiUiLoadFeedResourcesEpic",
"payload": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState"
}
}
]
}You can use the following script to copy the metadata for the application state directly, with the name of your choice:
const APPLICATION_STATE_NAME = '<name of you application state>';
const getApplicationStateMetadata = (feedApplicationStateId) => {
return `{
"type": "GenAiUiFeedApplicationState",
"effectTriggers": [
{
"trigger": "${feedApplicationStateId}.POLL_RESULT",
"payloadStrategy": "MERGE",
"effectType": "GenAiUiEpicPollFeedQuery",
"payload": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${feedApplicationStateId}"
}
},
{
"trigger": "${feedApplicationStateId}.LOAD_RESULT_RESOURCES",
"payloadStrategy": "MERGE",
"effectType": "GenAiUiLoadFeedResourcesEpic",
"payload": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${feedApplicationStateId}"
}
}
]
}`;
};
copy(getApplicationStateMetadata(APPLICATION_STATE_NAME));3. Create the JSON file for the Side Panel Chat
File name: LightBulbsSidePanelAgent.json:
{
"type": "UiSdlConnected<GenAiUiFeedSidePanel>",
"derivedProps": {
"uiSettings": {
"type": "UiSdlApplicationStateValueParam",
"id": "EnterpriseSearch.GenAiUiApplicationState",
"path": "uiSettings"
},
"plannerOptions": {
"type": "UiSdlApplicationStateValueParam",
"id": "EnterpriseSearch.GenAiUiApplicationState",
"path": "plannerOptions"
}
},
"component": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState",
"wrapWithMetadataId": true,
"initialExpandedWidth": "380px",
"fullyExpandedWidth": "900px",
"searchPlaceholder": "Ask anything about C3 AI",
"headerTitle": "C3 AI Agent",
"headerIcon": "sparkles",
"initiallyExpanded": true,
"agentConfigName": "Light Bulbs Agent",
"addResultIdQueryParam": true,
"welcomeMessage": "Welcome to C3 AI! Ready to get started?"
},
"effectTriggers": [
{
"trigger": "LightBulbsSidePanelAgentApplicationState.SEARCH_QUERY_SUBMIT",
"effectType": "GenAiUiEpicSubmitFeedQuery",
"payloadStrategy": "MERGE",
"payload": {
"doNotRedirect": true,
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState",
"feedId": "GenAiUi.FeedSidePanel",
"queryEntityTypeName": "Genai.Query.EnterpriseSearchQuery",
"queryEntityActionName": "createAndRankQuery"
}
},
{
"trigger": "SDL.DefaultSite.CURRENT_PATH_SET",
"effectType": "GenAiUiEpicTrackResultOnRedirect",
"payload": {
"feedId": "GenAiUi.FeedSidePanel",
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState"
}
},
{
"trigger": "GenAiUi.FeedSidePanel.INITIAL_RENDER",
"effectType": "GenAiUiEpicGetConversation",
"payloadStrategy": "MERGE",
"payload": {
"feedId": "GenAiUi.FeedSidePanel",
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState"
}
}
]
}You can use the following script to copy the metadata from your component directly:
const COMPONENT_NAME = '<name of you side panel component>';
const APPLICATION_STATE_NAME = '<name of you application state from previous step>';
const getComponentMetadata = (feedApplicationStateId, componentId) => {
return `{
"type": "UiSdlConnected<GenAiUiFeedSidePanel>",
"derivedProps": {
"uiSettings": {
"type": "UiSdlApplicationStateValueParam",
"id": "EnterpriseSearch.GenAiUiApplicationState",
"path": "uiSettings"
},
"plannerOptions": {
"type": "UiSdlApplicationStateValueParam",
"id": "EnterpriseSearch.GenAiUiApplicationState",
"path": "plannerOptions"
}
},
"component": {
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${APPLICATION_STATE_NAME}",
"wrapWithMetadataId": true,
"initialExpandedWidth": "380px",
"fullyExpandedWidth": "900px",
"searchPlaceholder": "Ask anything about C3 AI",
"headerTitle": "C3 AI Agent",
"headerIcon": "sparkles",
"initiallyExpanded": true,
"agentConfigName": "Light Bulbs Agent",
"addResultIdQueryParam": true,
"welcomeMessage": "Welcome to C3 AI! Ask me questions"
},
"effectTriggers": [
{
"trigger": "${APPLICATION_STATE_NAME}.SEARCH_QUERY_SUBMIT",
"effectType": "GenAiUiEpicSubmitFeedQuery",
"payloadStrategy": "MERGE",
"payload": {
"doNotRedirect": true,
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${APPLICATION_STATE_NAME}",
"feedId": "${COMPONENT_NAME}",
"queryEntityTypeName": "Genai.Query.EnterpriseSearchQuery",
"queryEntityActionName": "createAndRankQuery"
}
},
{
"trigger" : "SDL.DefaultSite.CURRENT_PATH_SET",
"effectType": "GenAiUiEpicTrackResultOnRedirect",
"payload": {
"feedId": "${COMPONENT_NAME}",
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${APPLICATION_STATE_NAME}"
}
},
{
"trigger": "${COMPONENT_NAME}.INITIAL_RENDER",
"effectType": "GenAiUiEpicGetConversation",
"payloadStrategy": "MERGE",
"payload": {
"feedId": "${COMPONENT_NAME}",
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "${APPLICATION_STATE_NAME}"
}
}
]
}`;
};
copy(getComponentMetadata(APPLICATION_STATE_NAME, COMPONENT_NAME));4. Instantiate the Side Panel Chat in the pages where you want to use it
- If your page is using a layout different than
UiSdlGridLayout, the recommended approach is to add the side panel as a child (similar to how modals are instantiated). Make sure the application state you instantiated for the side panel is references, and also the GenAI base application state ("EnterpriseSearch.GenAiUiApplicationState"). Example:
{
"type": "UiSdlConnected<LightBulbsLayoutPage>",
"applicationStateRef": {
"type": "[UiSdlApplicationStateRef]",
"value": [
{
// Base GenAI Application State instance
"type": "UiSdlApplicationStateRef",
"id": "EnterpriseSearch.GenAiUiApplicationState"
},
{
// Side Panel Chat Application instance
"type": "UiSdlApplicationStateRef",
"id": "LightBulbsSidePanelAgentApplicationState"
}
]
},
"component": {
"className": "light-bulbs-page",
"navigation": {
"id": "LightBulbsDynamicNavMenuRenderer"
},
"pageTitle": {
"id": "LightBulbsPageTitleDynamicRenderer"
},
"content": {
"id": "LightBulbsPageContent"
},
"children": [
{
// Side Panel instance
"id": "LightBulbsSidePanelAgent"
}
]
}
}- If your page is using
UiSdlGridLayout, the recommended approach is to add the side panel as a detached field. Make sure the application state you instantiated for the side panel is references, and also the GenAI base application state ("EnterpriseSearch.GenAiUiApplicationState"). Example:
{
"type": "UiSdlConnected<UiSdlGridLayout>",
"applicationStateRef": {
"type": "[UiSdlApplicationStateRef]",
"value": [
{
// Base GenAI Application State instance
"type": "UiSdlApplicationStateRef",
"id": "EnterpriseSearch.GenAiUiApplicationState"
},
{
// Side Panel Chat Application instance
"type": "UiSdlApplicationStateRef",
"id": "LightBulbsSidePanelAgentApplicationState"
}
]
},
"component": {
"gridStyle": "FIXED",
"fixedSize": "MEDIUM",
"wrapWithMetadataId": true,
"header": {
"type": "UiSdlGridLayoutHeader",
"children": [
{
"type": "UiSdlComponentContainer",
"childComponent": {
"id": "LightBulbsPageTitleDynamicRenderer"
}
}
]
},
"navMenu": {
"id": "LightBulbsDynamicNavMenuRenderer"
},
"children": [
{
"id": "LightBulbsPageContent"
}
],
"detachedFields": [
{
// Side Panel instance
"id": "LightBulbsSidePanelAgent"
}
]
}
}Advanced Configurations
The side panel component is highly customizable, and all the options are documented in GenAiUiFeedSidePanel. Below we introduce a few common scenarios where several options are offered for customization.
Expand behavior of Side Panel
The Side Panel has three different collapse states:
- Collapsed: The side panel is closed and the user can't interact with the agents. When the user clicks the expand button, the side panel goes to the expanded state.
- Expanded: The side panel is open and the user can interact with the agent. The width of the expanded side panel is determined by the
initialExpandedWidthprop of the JSON instance ofGenAiUiFeedSidePanel. From this state, the user can either collapse the side panel, or fully expand it. - Fully Expanded: The side panel is open and the user can interact with the agent. The width of the expanded side panel is determined by the
fullyExpandedWidthprop of the JSON instance ofGenAiUiFeedSidePanel. From this state, the user can either collapse the side panel, or go back to the expanded state. This state is recommended for consumption of complex grids and visualizations.
Side Panel as an overlay
In the example from this tutorial, when the side panel is collapsed or expanded, the rest of content of the page is re-adjusted with an animation. While this ensures the user sees all the page content while interacting with the agent, it can be problematic for complex dashboards.
In those scenarios, the side panel can be configured to work as an overlay on the page. Doing so, the side panel will become a layer on top of the page content, avoiding disruptions on the main layouts, although by hiding some of the screen are to the user if the side panel isn't collapsed.
To define the side panel behavior you can pass in a behavior prop to the JSON instance of GenAiUiFeedSidePanel which can take the values overlay or block.
Track Side Panel session between page refresh and navigation
You can determine whether a page refresh or navigation should preserve the current conversation in the side panel by setting the addResultIdQueryParam in the JSON instance of the side panel component. If It's set to true, a query parameter will be added to the route every time the user starts a conversation. This query parameter (resultId) allows to track the conversation and to reload it after a page refresh.
Notice that, if addResultIdQueryParam is set to true It's required that the effect trigger for GenAiUiEpicTrackResultOnRedirect is present on the JSON, just as It's in the example from this tutorial:
// LightBulbsSidePanelAgentApplicationState.json
"effectTriggers": [
// ...
{
"trigger": "SDL.DefaultSite.CURRENT_PATH_SET",
"effectType": "GenAiUiEpicTrackResultOnRedirect",
"payload": {
"feedId": "GenAiUi.FeedSidePanel",
"applicationStateId": "EnterpriseSearch.GenAiUiApplicationState",
"feedApplicationStateId": "LightBulbsSidePanelAgentApplicationState"
}
}
]
// ...Customize the agent used for queries from the side panel
You can define a specific agent to which queries from the side panel should be routed. To do so, define an agentConfigName in the GenAiUiFeedSidePanel JSON instance. If none is defined, the search bar will render the agent selector and the user can choose the agent manually.
Notice agentConfigName works both when the app is using GenaiCore.Agent.Deployment and when it's using Genai.Agent.Persistable agents, which is driven by useGenaiCoreAgents in GenAiUiConfig.
To learn more about how to configure agents, see Dynamic Agent Overview.
Customize UI elements of the Side Panel
You can customize these elements from the Side Panel UI just with props in the GenAiUiFeedSidePanel JSON instance.
- Title: determined by
headerTitle. It can be a translation key, in which case translation will be handled automatically. - Icon: determined by
headerIcon.It should be an icon available on SDL (Font Awesome). - Search Bar placeholder: determined by the
searchPlaceholder. It can be a translation key, in which case translation will be handled automatically. - Welcome Message: determined by
welcomeMessage. This message is displayed when there are no results in the feed, providing a friendly introduction to users.
Configure Suggested Queries for the Side Panel
You can configure suggested queries to appear in the side panel to help users get started with common or recommended queries. To enable this feature, add the dataSpec configuration in your GenAiUiFeedSidePanel JSON instance:
{
"type": "UiSdlConnected<GenAiUiFeedSidePanel>",
"component": {
// ... other configurations ...
"dataSpec": {
"dataType": "Genai.SeededQuery",
"advancedDataSpec": {
"actionName": "fetch",
"actionArgs": {
"spec": {
"include": "baseQuery, nonTranslatedQuery, category",
"filter": "intersects()"
}
}
},
"dataTransforms": ["UiSdlTransformFetchResultToArray"]
}
}
}This configuration will:
- Fetch suggested queries from your
Genai.SeededQueryentities - Display them in the side panel when appropriate
- Transform the results into an array format for display
The suggested queries feature provides users with quick access to common queries and helps them discover the capabilities of your AI agent.
Add custom styling for the Side Panel
It's possible to add custom CSS styling to the side panel for each integration. To do so, pass a className prop to the GenAiUiFeedSidePanel JSON instance. You can reference that class name from your stylesheet to add additional CSS rules.