Skip to content

new slot needed for after each message #646

@ethanwinters

Description

@ethanwinters

Overview

Add support for custom footer slots on bot messages to enable developers to add action buttons (copy, regenerate, share, etc.) alongside existing feedback controls.

Problem Statement

Users want to add custom action buttons to the footer of bot messages, but currently there's no straightforward way to inject custom UI elements into this area. Common use cases include:

  • Copy button: Copy message content to clipboard
  • Regenerate button: Re-run the query to get a new response
  • Share button: Share message content via external services
  • Custom actions: Application-specific actions related to a message

Proposed Solution

Extend GenericItemMessageOptions to support custom footer slots, following the same pattern as the existing feedback option.

Type Definitions

export interface GenericItemMessageOptions {
  feedback?: GenericItemMessageFeedbackOptions;
  custom_footer_slot?: GenericItemCustomFooterSlotOptions; // NEW
}

export interface GenericItemCustomFooterSlotOptions {
  /**
   * Unique identifier for this footer slot. Used to match with the render function.
   */
  slot_name: string;
  
  /**
   * Whether to show the custom footer slot.
   */
  is_on?: boolean;
  
  /**
   * Optional additional data to pass to the render function. This can contain
   * any custom metadata that the frontend needs to render the footer appropriately.
   */
  additional_data?: Record<string, unknown>;
}

New Render Prop

Add a new render prop to ChatContainerProps:

interface ChatContainerProps {
  // ... existing props
  
  /**
   * Render function for custom footer slots. Called for each message that has
   * custom_footer_slot configured in its message_item_options.
   */
  renderMessageFooter?: (
    slotName: string,
    message: MessageResponse,
    messageItem: GenericItem,
    instance: ChatInstance,
    additionalData?: Record<string, unknown>
  ) => ReactNode | null;
}

Usage Example

Backend/Message Processing:

{
  "response_type": "text",
  "text": "Here's your answer",
  "message_item_options": {
    "feedback": { 
      "is_on": true, 
      "id": "feedback-1" 
    },
    "custom_footer_slot": { 
      "is_on": true, 
      "slot_name": "footer-msg-123",
      "additional_data": {
        "allow_regenerate": true,
        "allow_copy": true,
        "custom_action_url": "https://example.com/share"
      }
    }
  }
}

Frontend:

<ChatContainer
  renderMessageFooter={(slotName, message, messageItem, instance, additionalData) => {
    return (
      <div className="custom-footer-actions">
        {additionalData?.allow_copy && (
          <button 
            onClick={() => {
              navigator.clipboard.writeText(messageItem.text);
            }}
          >
            Copy
          </button>
        )}
        {additionalData?.allow_regenerate && (
          <button 
            onClick={() => {
              instance.send({ text: message.input.text });
            }}
          >
            Regenerate
          </button>
        )}
        {additionalData?.custom_action_url && (
          <button 
            onClick={() => {
              window.open(additionalData.custom_action_url, '_blank');
            }}
          >
            Share
          </button>
        )}
      </div>
    );
  }}
/>

Design Rationale

Why This Approach?

  1. Consistent with existing patterns: Follows the same structure as feedback in message_item_options
  2. Clear separation of concerns: Message structure controls "where" (which messages get footers), render prop controls "what" (the actual content)
  3. Per-message control: Backend or frontend message processing can decide which messages get footer slots
  4. Performance: Only creates slots for messages that explicitly opt-in
  5. Flexible metadata: additional_data allows backend to pass custom configuration without changing the type definition
  6. Future-proof: Natural evolution path when native controls (copy, regenerate) are added

Why additional_data?

The additional_data field provides flexibility for:

  • Backend to control which actions are available per message
  • Passing custom URLs, IDs, or configuration
  • Enabling/disabling specific actions based on message context
  • Supporting application-specific metadata without type changes

Future Evolution Path

When copy/regenerate become native controls, they can be added to the same structure:

export interface GenericItemCustomFooterSlotOptions {
  slot_name?: string;        // For custom content (optional when using native controls)
  is_on?: boolean;
  show_copy?: boolean;       // Native copy button
  show_regenerate?: boolean; // Native regenerate button
  additional_data?: Record<string, unknown>; // Still available for custom controls
}

This allows:

  • Custom content only
  • Native controls only
  • Both custom content and native controls together
  • Custom metadata for either approach

Implementation Considerations

Rendering Location

The custom footer slot should appear:

  • After the main message content
  • Alongside or near the feedback buttons
  • Before any chain-of-thought or other metadata

Slot Management

Similar to how user_defined responses work:

  1. When a message with custom_footer_slot.is_on = true is received, create a slot element
  2. Use the slot_name to uniquely identify the slot
  3. Call renderMessageFooter with the slot context and additional_data
  4. Render the returned React content into the slot via portal

Web Component Support

Ensure this works with both:

  • <ChatContainer> (React)
  • <cds-aichat-container> (Web Component)

Related Code References

Open Questions

  1. How do we handle if the footer content is too large?

Success Criteria

  • Developers can add custom footer slots via message_item_options.custom_footer_slot
  • renderMessageFooter prop works in both React and Web Component implementations
  • additional_data is properly passed through to the render function
  • Footer slots appear in the correct location relative to feedback buttons
  • Performance is acceptable (no unnecessary slot creation)
  • Documentation includes usage examples with additional_data
  • Demo/examples showcase common use cases (copy, regenerate, conditional actions)
  • Design accommodates future native controls without breaking changes

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions