> ## Documentation Index
> Fetch the complete documentation index at: https://developer.watson-orchestrate.ibm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# userDefinedResponse

Triggered when a response contains an unrecognized or `user_defined` response type. Use this event to render custom UI elements for specialized content types that are not natively supported by the chat widget.

The `userDefinedResponse` event enables the following use cases:

1. **Custom response types**: Handle any unrecognized response types with custom rendering logic.
2. **Tool-generated widgets**: Render custom widgets returned by tools that use the `user_defined` response type. For more information, see [Structuring custom widgets](#structuring-custom-widgets).

<Note>
  The `user_defined` response type from tools is supported only in embedded chat. The webpage builder is responsible for implementing the rendering logic.
</Note>

## Event properties

<ParamField path="type" type="string" required>
  Always `'userDefinedResponse'`.
</ParamField>

<ParamField path="contentItem" type="object" required>
  The individual message item with the `user_defined` response type. This might contain the widget data directly or reference it from the message's `_meta` field.
</ParamField>

<ParamField path="contentItem.text" type="string">
  Text content of the item, if any.
</ParamField>

<ParamField path="contentItem.user_defined" type="object">
  Widget data when provided directly in the content item. This approach is still fully supported alongside the new `_meta` approach for tool-generated widgets.
</ParamField>

<ParamField path="message" type="object" required>
  The complete message containing the user-defined response item.
</ParamField>

<ParamField path="message._meta" type="object">
  Metadata field that might contain widget data under `com.ibm.orchestrate/widget`. Use this approach for tool-generated widgets.
</ParamField>

<ParamField path="message._meta['com.ibm.orchestrate/widget']" type="object">
  Widget metadata object containing the `user_defined` widget data.
</ParamField>

<ParamField path="message._meta['com.ibm.orchestrate/widget'].response_type" type="string">
  Must be `'user_defined'` for custom widgets.
</ParamField>

<ParamField path="message._meta['com.ibm.orchestrate/widget'].user_defined" type="object">
  The actual widget data object containing all custom properties.
</ParamField>

<ParamField path="hostElement" type="HTMLElement" required>
  The DOM element where you can insert custom content. Use this to render your custom widget HTML.
</ParamField>

## Structuring custom widgets

<Note>
  Custom widgets in tool responses is a custom solution that operates independently of standardized UI protocols such as MCP-UI or A2UI. Application builders are responsible for implementing the rendering logic and managing security considerations.
</Note>

When a tool returns a `user_defined` widget, the data is available in the `_meta` field. For information on how to create tools that return `user_defined` widgets, including code examples and structure requirements, see [Custom widgets with `user_defined`](../../tools/widget_integration#custom-widgets-with-user_defined).

### Widget data location

You can find the widget data in multiple locations depending on the implementation:

1. **Direct content approach**: `event.contentItem.user_defined`\
   Use when widget data is provided directly in the content item.

2. **Tool-generated widgets**: `event.message._meta['com.ibm.orchestrate/widget'].user_defined`\
   Use when tools return widgets through the `_meta` parameter.

<Tip>
  You can check all locations to ensure compatibility with both approaches, for example:

  ```javascript theme={null}
  const widgetMeta = event.message?._meta?.['com.ibm.orchestrate/widget'];
  const widgetData =
      event.contentItem?.user_defined ||
      widgetMeta?.user_defined ||
      (widgetMeta?.response_type === 'user_defined' ? widgetMeta : null);
  ```
</Tip>

### State persistence

When loading from message history, the system automatically filters widgets from the content array to prevent duplicate rendering. The system preserves the widget data in `message_state` for reference, ensuring that widget information remains available for context without causing duplicate UI elements.

## Examples

The following example demonstrates rendering a custom HTML card with structured content and interactive elements for handling user-defined response type.

```javascript theme={null}
instance.on('userDefinedResponse', (event, instance) => {
    // Log the received event for debugging purposes
    console.log('User-defined response received:', event.contentItem);
    
    // Render a custom HTML card with header, body, and footer sections
    // This approach is useful for displaying structured content like alerts, notifications, or custom cards
    // The event.hostElement is the DOM container where the custom content will be injected
    event.hostElement.innerHTML = `
        <div class="custom-response">
            <div class="custom-header">
                <h3>Custom Content</h3>
            </div>
            <div class="custom-body">
                <!-- Extract text from contentItem with fallback to default message if not present -->
                <p>${event.contentItem?.text || 'No content available'}</p>
            </div>
            <div class="custom-footer">
                <!-- Add interactive button that calls a custom handler function -->
                <button onclick="handleCustomAction()">Take Action</button>
            </div>
        </div>
    `;
});
```

The following example demonstrates how to handle custom widgets returned by tools through the `_meta` field. The handler extracts widget data from multiple possible locations in the event object, normalizes the properties with fallback values, renders a fully styled custom widget with interactive elements, and includes a fallback for cases where widget data is unavailable.

```javascript [expandable] theme={null}
/**
 * Handler for custom user-defined response types in the webchat.
 * This function processes widget data from tool responses and renders custom HTML.
 *
 * @param {Object} event - The userDefinedResponse event object containing message and content data
 * @param {Object} instance - The webchat instance for accessing chat functionality
 */
function userDefinedResponseHandler(event, instance) {
    console.log('userDefinedResponse event', event);
    
    // Extract widget data from multiple possible locations in the event object
    // The widget data can be stored in different places depending on how it was sent:
    // 1. event.contentItem.user_defined - Direct content item property
    // 2. event.message._meta['com.ibm.orchestrate/widget'].user_defined - Metadata property
    // 3. event.message._meta['com.ibm.orchestrate/widget'] - Root metadata if response_type is 'user_defined'
    const widgetMeta = event.message?._meta?.['com.ibm.orchestrate/widget'];
    const widgetData =
        event.contentItem?.user_defined ||
        widgetMeta?.user_defined ||
        (widgetMeta?.response_type === 'user_defined' ? widgetMeta : null);
    
    // Check if widget data was found in any of the possible locations
    if (widgetData) {
        console.log('Rendering widget from tool data:', widgetData);
        
        // Extract and normalize widget properties with fallback values
        // Each property checks the widget data and provides a sensible default if not present
        const title = widgetData.title || 'Default Custom Widget';
        const message = widgetData.text || widgetData.description || 'No content available';
        const widgetType = widgetData.user_defined_type || 'custom_component';
        
        // Convert timestamp to human-readable format if present, otherwise set to null
        const timestamp = widgetData.timestamp
            ? new Date(widgetData.timestamp).toLocaleString()
            : null;
        
        // Extract optional custom fields from the widget data
        const customField = widgetData.custom_field || null;
        
        // Determine if the widget should be interactive (strict boolean check)
        const interactive = widgetData.interactive === true;
        
        // Get the action URL for the button, defaulting to '#' if not provided
        const actionUrl = widgetData.action_url || '#';
        
        // Render the custom widget HTML with inline styles
        // This creates a styled card with gradient background, title, message content,
        // metadata display, and an interactive button
        event.hostElement.innerHTML = `
            <div style="border: 2px solid #667eea; border-radius: 8px; padding: 16px; background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);">
                <h3 style="color: #667eea; margin: 0 0 12px 0; font-size: 18px;">
                    🎨 ${title}
                </h3>
                <div style="background-color: #f0f4ff; color: #333; padding: 12px; border-radius: 6px; margin-bottom: 12px;">
                    <p style="margin: 0; font-weight: 600;">Message Content:</p>
                    <p style="margin: 8px 0 0 0;">${message}</p>
                </div>
                <div style="background-color: #fff; padding: 12px; border-radius: 6px; border-left: 4px solid #667eea;">
                    <p style="margin: 0 0 8px 0; font-size: 14px; color: #666;">
                        This widget is rendered in the browser from tool-provided structured data.
                    </p>
                    <p style="margin: 0; font-size: 12px; color: #666;">
                        <strong>Type:</strong> ${widgetType}
                        ${timestamp ? `<br><strong>Timestamp:</strong> ${timestamp}` : ''}
                        ${customField ? `<br><strong>Custom field:</strong> ${customField}` : ''}
                        <br><strong>Interactive:</strong> ${interactive ? 'Yes' : 'No'}
                    </p>
                </div>
                <button
                    type="button"
                    data-widget-link="${actionUrl}"
                    style="display: block; width: 100%; margin-top: 12px; padding: 10px; background: #764ba2; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;"
                >
                    ✨ Fully Customizable Content ✨
                </button>
            </div>
        `;
        
        // Add event listeners for interactive button behavior
        // Query the DOM for the button element using its data attribute
        const actionButton = event.hostElement.querySelector('[data-widget-link]');
        if (actionButton) {
            // Click handler: Opens the action URL in a new tab if it's not the default '#'
            actionButton.addEventListener('click', () => {
                if (actionButton.dataset.widgetLink && actionButton.dataset.widgetLink !== '#') {
                    window.open(actionButton.dataset.widgetLink, '_blank');
                }
            });
            
            // Hover effect: Darken the button background on mouseover
            actionButton.addEventListener('mouseover', () => {
                actionButton.style.background = '#5a3a82';
            });
            
            // Hover effect: Restore original button background on mouseout
            actionButton.addEventListener('mouseout', () => {
                actionButton.style.background = '#764ba2';
            });
        }
        
        // Exit early since we successfully rendered the widget
        return;
    }
    
    // Fallback rendering when no widget data is found
    // This displays a simple orange box with any available text content
    console.log('No widget data found, using fallback rendering');
    event.hostElement.innerHTML = `
        <div style="background-color:orange;color:white;padding:10px;">
            <p>${event.contentItem?.text || '[No message content]'}</p>
        </div>
    `;
}

// Register the event handler with the webchat instance
// This tells the webchat to call userDefinedResponseHandler whenever a userDefinedResponse event occurs
instance.on('userDefinedResponse', userDefinedResponseHandler);
```

<Card title="Do you need practical examples?" icon="text-align-start" href="https://www.ibm.com/docs/SSAVQO/deploy/web_chat_agent/tutorials/tutorials_lp.html" arrow="true" cta="Learn with walk-throughs" horizontal>
  Learn how to apply the features available for embedded chat into your implementation with guidance and examples.
</Card>
