Skip to main content
Version: 2026.1

Rich Chat Widgets

Rich chat widgets are interactive React components the LLM can drop into the chat during a response. They turn "here is a URL" into a clickable element link, "here are 15 cars I found" into an inline table, "I want to make these changes" into a proposal card with a diff modal. Widgets contain no server-side business logic: some render the arguments passed in the tool call directly, others (notably the proposal widgets) fetch their payload from the Studio Backend via RTK Query on mount — by design, so the LLM never has to relay large or sensitive payloads through the conversation.

This page is the canonical catalog of every widget that ships with the bundle. For how to add your own, see Extending → Rich Chat Widgets.

The widget catalog

Nine widgets ship today. The Triggered by column shows what makes the widget appear. Five are LLM-callable display tools — the model chooses whether and what to show:

WidgetWidget type keyTriggered byWhat the user sees
Open elementelement-linkLLM tool pimcore_open_elementClickable button that opens a data object, asset, or document in Studio. Icon varies by element type.
Switch agentagent-switchLLM tool pimcore_switch_agentPill button suggesting a different agent. One click switches — see Agents and Switching.
Asset previewasset-previewLLM tool show_asset_previewInline image gallery of asset thumbnails. Click to open in Studio.
Data-object previewdata-object-previewLLM tool show_data_object_previewPreview cards with a few key attributes and an "open" button.
Data tabledata-tableLLM tool show_data_tableInteractive, sortable, searchable table for search results.

The remaining four are server-emitted — they are not LLM tools. A freshly created proposal should always be surfaced, so the agent-server emits the proposal widget automatically from the minted proposalId the moment a propose_* tool succeeds (on tool.execution_complete). There is no show_*_proposal tool and no widget YAML for these:

WidgetWidget type keyTriggered byWhat the user sees
Data-object proposalproposal-data-objectServer, after propose_data_object_updateHITL proposal card with approve / reject / refine actions and a diff modal. See HITL Proposals.
Asset metadata proposalproposal-asset-metadataServer, after propose_asset_metadata_updateHITL proposal card for asset metadata edits.
Document proposalproposal-documentServer, after propose_document_updateHITL proposal card for document edits. The diff modal carries Preview / Edit / Settings tabs and a target-group control when the personalization-bundle is installed — see Architecture → Document Schema → Target-group variants.
Tag proposalproposal-tagsServer, after propose_tag_assignmentHITL proposal card for tag assignments.
Agent-template proposalproposal-agent-templateServer, after propose_agent_template / …_deletionHITL proposal card for agent Twig-template create / update / delete.

Two further widgets are server-emitted outside the proposal flow:

WidgetWidget type keyTriggered byWhat the user sees
Ask userask-userServer, on ask_user toolInline question with choice buttons and/or freeform input. See Interactive Input.
Proposal resultproposal-resultServer, after resolveRead-only status chip showing whether a proposal applied, was rejected, or errored.

Enabling widgets for an agent

The LLM-callable display widgets are opt-in per agent. In the YAML under richChatWidgets, list the widgets the agent is allowed to trigger:

richChatWidgets:
- pimcore_open_element
- show_data_object_preview
- pimcore_switch_agent

If an agent calls a widget it is not configured for, the call is rejected. A widget not in the agent's list never appears in chat, regardless of what the LLM asks for.

The proposal widgets are not listed here — they are server-emitted, so simply granting an agent the matching propose_* tool is enough for its review widget to appear automatically.

How widgets reach the screen

The mechanism — for when you are debugging why a widget didn't render — is briefly:

LLM-callable display widgets:

  1. LLM calls a widget tool (e.g. show_data_table).
  2. The agent-server intercepts the SDK's tool.execution_start event before the tool would actually run.
  3. Server checks the tool name against the widget registry; on match, it emits a widget SSE event with the payload.
  4. A confirmation string from the widget's handlerTemplate is returned to the LLM as the tool result, so the LLM knows the widget rendered.
  5. The frontend's widget renderer registry maps widgetType to a React component and mounts it inline in the message stream.

Server-emitted proposal widgets:

  1. LLM calls a propose_* tool, which mints proposalIds and stores the payload server-side.
  2. The PHP propose tool embeds a server-only widget directive (widgetType + proposalIds + summary) in its tool result, alongside an LLM-visible stop-signal.
  3. The agent-server intercepts the SDK's tool.execution_complete event, parses the directive out of the result, and emits the same widget SSE event — no widget registry / YAML lookup involved.
  4. The frontend renders it via the same widgetType → component registry. The widget fetches its diff from the Studio Backend by proposalId.

Either way the widget appears instantly, before the LLM continues, because the server short-circuits the normal tool-call path. The widget event is also persisted as a message part, so it re-renders on reopen.

Further reading