🎨 ADVANCED UI COMPONENTS CHEAT SHEET

🎨 ADVANCED UI COMPONENTS CHEAT SHEET #

Ultra-Condensed Enlightenment Guide - Red 3.5+ & Discord.py 2.3+ Compliant


🚀 DISCORD.UI ARCHITECTURE MASTERY (Red 3.5+) #

Modern UI Component Hierarchy #

┌─────────────────────────────────────────────────────────────┐
│                     BOT COMMAND LAYER                       │
│  • Hybrid Commands     • App Commands     • Context Menus   │
├─────────────────────────────────────────────────────────────┤
│                    VIEW CONTAINER LAYER                     │
│  • discord.ui.View    • Red SimpleMenu   • Custom Views     │
├─────────────────────────────────────────────────────────────┤
│                   COMPONENT LAYER                           │
│  • Buttons            • Select Menus     • Modals           │
│  • Text Inputs        • User Selects     • Role Selects     │
├─────────────────────────────────────────────────────────────┤
│                  INTERACTION LAYER                          │
│  • interaction.response  • Followups    • Deferrals         │
├─────────────────────────────────────────────────────────────┤
│                    DISCORD API LAYER                        │
│  • Gateway Events     • HTTP Responses   • Rate Limits      │
└─────────────────────────────────────────────────────────────┘

Core UI Framework (Red 3.5+ Compliant) #

# Modern Red 3.5+ UI Implementation
from redbot.core import commands, Config
from redbot.core.utils.views import ConfirmView, SimpleMenu
import discord
import asyncio
import logging

log = logging.getLogger("red.mycog.ui")

# GDPR Compliance for UI interactions
__red_end_user_data_statement__ = (
    "This cog stores interaction data temporarily for UI state management. "
    "Data is automatically cleaned up after view timeouts."
)

class ModernUICog(commands.Cog):
    """Red 3.5+ compliant UI cog with all modern patterns"""
    
    def __init__(self, bot):
        self.bot = bot
        self.config = Config.get_conf(self, identifier=1234567890, force_registration=True)
        self.active_views = {}  # Track views for cleanup
        
        # UI-specific defaults
        self.config.register_guild(
            ui_theme="default",
            ui_timeout=300,
            ephemeral_responses=True
        )
    
    async def cog_load(self):
        """Initialize UI system on cog load"""
        log.info("UI system loading...")
        await self.setup_persistent_views()
    
    async def cog_unload(self):
        """Clean up all active views on unload"""
        log.info("UI system unloading...")
        for view in list(self.active_views.values()):
            view.stop()
        self.active_views.clear()
        
    async def red_delete_data_for_user(self, kwargs):
        """GDPR compliance - remove user UI data"""
        user_id = kwargs.get("user_id")
        if user_id:
            for view_id, view in self.active_views.items():
                if hasattr(view, 'author') and view.author.id == user_id:
                    view.stop()

# REQUIRED: Async setup function (Red 3.5+)
async def setup(bot):
    """Load the cog - MUST BE ASYNC in Red 3.5+"""
    cog = ModernUICog(bot)
    await bot.add_cog(cog)

# Component Limits (Discord.py 2.3+)
COMPONENT_LIMITS = {
    "buttons_per_row": 5,
    "select_menus_per_row": 1,
    "rows_per_view": 5,
    "total_components": 25,
    "select_options": 25,
    "modal_text_inputs": 5,
    "embed_fields": 25,
    "embed_characters": 6000
}
  • 🎯 Architecture: Layered UI system from commands to Discord API
  • 🔧 Red Integration: Native Red utility classes and patterns
  • 📊 State Management: Persistent view tracking and cleanup
  • 🎨 Modern Components: Latest discord.py 2.3+ component types
  • 🔄 Lifecycle: Proper view lifecycle with Red 3.5+ patterns
  • 🛡️ GDPR Compliance: User data cleanup and privacy protection
  • 📦 Resource Management: View tracking and automatic cleanup
  • 🎯 Error Boundaries: Comprehensive error handling throughout

🔲 MODERN BUTTON SYSTEM MASTERY #

Advanced Button Implementation (Discord.py 2.3+) #

class ProfessionalButtonView(discord.ui.View):
    """Showcase all modern button patterns and styles"""
    
    def __init__(self, author, *, timeout=180):
        super().__init__(timeout=timeout)
        self.author = author
        self.result = None
        
    @discord.ui.button(
        label="Primary Action", 
        style=discord.ButtonStyle.primary,
        emoji="🚀",
        row=0
    )
    async def primary_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Primary action button - main call-to-action"""
        await interaction.response.send_message(
            "🚀 Primary action executed!", 
            ephemeral=True
        )
    
    @discord.ui.button(
        label="Secondary Info", 
        style=discord.ButtonStyle.secondary,
        emoji="ℹ️",
        row=0
    )
    async def secondary_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Secondary button - informational actions"""
        embed = discord.Embed(
            title="ℹ️ Information",
            description="This is additional information about the action.",
            color=0x5865F2
        )
        await interaction.response.send_message(embed=embed, ephemeral=True)
    
    @discord.ui.button(
        label="Confirm", 
        style=discord.ButtonStyle.success,
        emoji="✅",
        row=1
    )
    async def success_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Success/confirmation button"""
        self.result = True
        self.stop()
        await interaction.response.edit_message(
            content="✅ Action confirmed! View closed.",
            view=None
        )
    
    @discord.ui.button(
        label="Danger Zone", 
        style=discord.ButtonStyle.danger,
        emoji="⚠️",
        row=1
    )
    async def danger_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Destructive action with double confirmation"""
        confirm = ConfirmView(interaction.user, timeout=30)
        await interaction.response.send_message(
            "⚠️ DANGER: This action cannot be undone. Are you sure?",
            view=confirm,
            ephemeral=True
        )
        await confirm.wait()
        
        if confirm.result:
            await interaction.followup.send("💥 Destructive action completed!", ephemeral=True)
        else:
            await interaction.followup.send("❌ Action cancelled.", ephemeral=True)
    
    @discord.ui.button(
        label="🔗 Documentation", 
        style=discord.ButtonStyle.link,
        url="https://docs.pycord.dev/en/stable/",
        row=2
    )
    async def link_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Link button - no callback needed, opens URL directly"""
        pass  # Link buttons don't trigger callbacks
    
    @discord.ui.button(
        label="Toggle", 
        style=discord.ButtonStyle.secondary,
        emoji="🔄",
        row=2
    )
    async def toggle_button(self, interaction: discord.Interaction, button: discord.ui.Button):
        """Toggle button with state management"""
        if button.style == discord.ButtonStyle.secondary:
            button.style = discord.ButtonStyle.success
            button.label = "Enabled"
            button.emoji = "✅"
            message = "Feature enabled!"
        else:
            button.style = discord.ButtonStyle.secondary
            button.label = "Toggle"
            button.emoji = "🔄"
            message = "Feature disabled!"
        
        await interaction.response.edit_message(
            content=f"🔄 {message}",
            view=self
        )
    
    async def interaction_check(self, interaction: discord.Interaction) -> bool:
        """Enhanced security check with Red patterns"""
        if interaction.user != self.author:
            await interaction.response.send_message(
                "❌ Only the command author can use these buttons.",
                ephemeral=True
            )
            return False
        return True
    
    async def on_timeout(self):
        """Red-style timeout handling"""
        try:
            for child in self.children:
                child.disabled = True
            if hasattr(self, 'message') and self.message:
                await self.message.edit(
                    content="⏰ This interaction has expired.",
                    view=self
                )
        except discord.HTTPException:
            pass

# Dynamic Button Management
class DynamicButtonView(discord.ui.View):
    """Advanced dynamic button management with runtime modification"""
    
    def __init__(self, author, max_buttons=20):
        super().__init__(timeout=300)
        self.author = author
        self.max_buttons = min(max_buttons, 20)  # Safety limit
        self.button_count = 0
        
    def add_dynamic_button(self, label, emoji=None, style=discord.ButtonStyle.secondary):
        """Add button at runtime with automatic row management"""
        if self.button_count >= self.max_buttons:
            return False
            
        button = discord.ui.Button(
            label=label,
            emoji=emoji,
            style=style,
            custom_id=f"dynamic_{self.button_count}",
            row=self.button_count // 5  # Auto-row calculation
        )
        
        async def button_callback(interaction):
            await interaction.response.send_message(
                f"You clicked: {label}",
                ephemeral=True
            )
        
        button.callback = button_callback
        self.add_item(button)
        self.button_count += 1
        return True
    
    def remove_button_by_label(self, label):
        """Remove button by label and rebalance rows"""
        for item in self.children.copy():
            if isinstance(item, discord.ui.Button) and item.label == label:
                self.remove_item(item)
                self.button_count -= 1
                self._rebalance_rows()
                return True
        return False
    
    def _rebalance_rows(self):
        """Rebalance button rows after removal"""
        buttons = [item for item in self.children if isinstance(item, discord.ui.Button)]
        for i, button in enumerate(buttons):
            button.row = i // 5

# Professional Navigation View
class NavigationView(discord.ui.View):
    """Red-style navigation with comprehensive controls"""
    
    def __init__(self, pages, *, author, timeout=300):
        super().__init__(timeout=timeout)
        self.pages = pages
        self.author = author
        self.current_page = 0
        self.max_pages = len(pages)
        
        if self.max_pages > 1:
            self._add_navigation_buttons()
            self._update_button_states()
    
    def _add_navigation_buttons(self):
        """Add all navigation buttons"""
        # First page
        self.first_btn = discord.ui.Button(emoji="⏮️", style=discord.ButtonStyle.secondary, row=0)
        # Previous page  
        self.prev_btn = discord.ui.Button(emoji="◀️", style=discord.ButtonStyle.primary, row=0)
        # Stop view
        self.stop_btn = discord.ui.Button(emoji="🛑", style=discord.ButtonStyle.danger, row=0)
        # Next page
        self.next_btn = discord.ui.Button(emoji="▶️", style=discord.ButtonStyle.primary, row=0)
        # Last page
        self.last_btn = discord.ui.Button(emoji="⏭️", style=discord.ButtonStyle.secondary, row=0)
        
        # Assign callbacks
        self.first_btn.callback = self._first_page
        self.prev_btn.callback = self._previous_page
        self.stop_btn.callback = self._stop_view
        self.next_btn.callback = self._next_page
        self.last_btn.callback = self._last_page
        
        # Add to view
        self.add_item(self.first_btn)
        self.add_item(self.prev_btn)
        self.add_item(self.stop_btn)
        self.add_item(self.next_btn)
        self.add_item(self.last_btn)
    
    async def _first_page(self, interaction: discord.Interaction):
        """Jump to first page"""
        if self.current_page == 0:
            return await interaction.response.defer()
        
        self.current_page = 0
        self._update_button_states()
        await self._update_page(interaction)
    
    async def _previous_page(self, interaction: discord.Interaction):
        """Go to previous page"""
        if self.current_page == 0:
            return await interaction.response.defer()
        
        self.current_page -= 1
        self._update_button_states()
        await self._update_page(interaction)
    
    async def _stop_view(self, interaction: discord.Interaction):
        """Stop the view"""
        self.stop()
        await interaction.response.edit_message(
            content="🛑 Navigation stopped.",
            view=None
        )
    
    async def _next_page(self, interaction: discord.Interaction):
        """Go to next page"""
        if self.current_page >= self.max_pages - 1:
            return await interaction.response.defer()
        
        self.current_page += 1
        self._update_button_states()
        await self._update_page(interaction)
    
    async def _last_page(self, interaction: discord.Interaction):
        """Jump to last page"""
        if self.current_page >= self.max_pages - 1:
            return await interaction.response.defer()
        
        self.current_page = self.max_pages - 1
        self._update_button_states()
        await self._update_page(interaction)
    
    def _update_button_states(self):
        """Update button enabled/disabled states"""
        if self.max_pages <= 1:
            return
            
        # Disable first/previous if on first page
        self.first_btn.disabled = self.prev_btn.disabled = (self.current_page == 0)
        
        # Disable next/last if on last page
        self.next_btn.disabled = self.last_btn.disabled = (self.current_page >= self.max_pages - 1)
    
    async def _update_page(self, interaction):
        """Update page content"""
        page_content = self.pages[self.current_page]
        
        if isinstance(page_content, discord.Embed):
            page_content.set_footer(text=f"Page {self.current_page + 1}/{self.max_pages}")
            await interaction.response.edit_message(embed=page_content, view=self)
        else:
            content = f"{page_content}\n\n📄 Page {self.current_page + 1}/{self.max_pages}"
            await interaction.response.edit_message(content=content, view=self)
  • 🎯 Five Styles: Primary, Secondary, Success, Danger, Link buttons
  • 🔧 Dynamic Management: Runtime button addition/removal with row balancing
  • 📊 State Tracking: Toggle buttons with visual state changes
  • 🎨 Navigation: Professional page navigation with all controls
  • 🔄 Auto-Layout: Automatic row management for up to 25 buttons
  • 🛡️ Security: Author-only interaction checks with ephemeral errors
  • 📦 Timeout Handling: Graceful timeout with button disabling
  • 🎯 Red Integration: Compatible with Red’s view utilities

📋 NEXT-GEN SELECT MENU MASTERY #

Modern Select Menu Types (Discord.py 2.3+) #

class ComprehensiveSelectView(discord.ui.View):
    """Showcase all modern select menu types"""
    
    def __init__(self, author, *, timeout=300):
        super().__init__(timeout=timeout)
        self.author = author
        
    @discord.ui.select(
        placeholder="Choose options...",
        options=[
            discord.SelectOption(
                label="Option 1", 
                value="opt1", 
                description="First option with description",
                emoji="1️⃣"
            ),
            discord.SelectOption(
                label="Option 2", 
                value="opt2", 
                description="Second option with description",
                emoji="2️⃣"
            ),
            discord.SelectOption(
                label="Option 3", 
                value="opt3", 
                description="Third option (pre-selected)",
                emoji="3️⃣",
                default=True  # Pre-selected option
            )
        ],
        min_values=1,
        max_values=2,  # Allow multiple selections
        row=0
    )
    async def string_select(self, interaction: discord.Interaction, select: discord.ui.Select):
        """Standard string select menu"""
        selected = ", ".join(select.values)
        await interaction.response.send_message(
            f"📋 You selected: {selected}",
            ephemeral=True
        )
    
    @discord.ui.user_select(
        placeholder="Select users...",
        min_values=1,
        max_values=3,
        row=1
    )
    async def user_select(self, interaction: discord.Interaction, select: discord.ui.UserSelect):
        """User select menu (Discord.py 2.3+)"""
        users = [user.display_name for user in select.values]
        user_list = ", ".join(users)
        await interaction.response.send_message(
            f"👥 Selected users: {user_list}",
            ephemeral=True
        )
    
    @discord.ui.role_select(
        placeholder="Select roles...",
        min_values=1,
        max_values=5,
        row=2
    )
    async def role_select(self, interaction: discord.Interaction, select: discord.ui.RoleSelect):
        """Role select menu (Discord.py 2.3+)"""
        roles = [role.name for role in select.values]
        role_list = ", ".join(roles)
        await interaction.response.send_message(
            f"🏷️ Selected roles: {role_list}",
            ephemeral=True
        )
    
    @discord.ui.channel_select(
        placeholder="Select channels...",
        channel_types=[
            discord.ChannelType.text,
            discord.ChannelType.voice,
            discord.ChannelType.category
        ],
        min_values=1,
        max_values=3,
        row=3
    )
    async def channel_select(self, interaction: discord.Interaction, select: discord.ui.ChannelSelect):
        """Channel select menu with type filtering (Discord.py 2.3+)"""
        channels = [f"#{channel.name}" for channel in select.values]
        channel_list = ", ".join(channels)
        await interaction.response.send_message(
            f"📺 Selected channels: {channel_list}",
            ephemeral=True
        )
    
    @discord.ui.mentionable_select(
        placeholder="Select users, roles, or both...",
        min_values=1,
        max_values=5,
        row=4
    )
    async def mentionable_select(self, interaction: discord.Interaction, select: discord.ui.MentionableSelect):
        """Mentionable select (users + roles) (Discord.py 2.3+)"""
        mentions = []
        for item in select.values:
            if isinstance(item, discord.Member):
                mentions.append(f"👤 {item.display_name}")
            elif isinstance(item, discord.Role):
                mentions.append(f"🏷️ {item.name}")
        
        mention_list = ", ".join(mentions)
        await interaction.response.send_message(
            f"🔖 Selected: {mention_list}",
            ephemeral=True
        )

# Smart Paginated Select Menu
class PaginatedSelectView(discord.ui.View):
    """Intelligent paginated select menu for large datasets"""
    
    def __init__(self, items, *, author, per_page=25, timeout=300):
        super().__init__(timeout=timeout)
        self.items = items
        self.author = author
        self.per_page = per_page
        self.current_page = 0
        self.max_pages = (len(items) + per_page - 1) // per_page
        
        self._build_current_page()
        
        if self.max_pages > 1:
            self._add_navigation()
    
    def _build_current_page(self):
        """Build select menu for current page"""
        # Remove existing select menu
        for item in self.children.copy():
            if isinstance(item, discord.ui.Select) and not isinstance(
                item, (discord.ui.UserSelect, discord.ui.RoleSelect, discord.ui.ChannelSelect)
            ):
                self.remove_item(item)
        
        # Calculate page items
        start_idx = self.current_page * self.per_page
        end_idx = min(start_idx + self.per_page, len(self.items))
        page_items = self.items[start_idx:end_idx]
        
        # Create options
        options = []
        for i, item in enumerate(page_items):
            if isinstance(item, dict):
                options.append(discord.SelectOption(
                    label=item.get("label", f"Item {start_idx + i + 1}"),
                    value=item.get("value", str(start_idx + i)),
                    description=item.get("description"),
                    emoji=item.get("emoji")
                ))
            else:
                options.append(discord.SelectOption(
                    label=str(item)[:100],  # Discord limit
                    value=str(start_idx + i)
                ))
        
        # Create select menu
        select_menu = discord.ui.Select(
            placeholder=f"Page {self.current_page + 1}/{self.max_pages} - Choose an item...",
            options=options,
            row=0
        )
        
        async def select_callback(interaction):
            selected_idx = int(select_menu.values[0])
            selected_item = self.items[selected_idx]
            await interaction.response.send_message(
                f"✅ Selected: {selected_item}",
                ephemeral=True
            )
        
        select_menu.callback = select_callback
        self.add_item(select_menu)
    
    def _add_navigation(self):
        """Add navigation buttons for multiple pages"""
        @discord.ui.button(emoji="◀️", style=discord.ButtonStyle.secondary, row=1)
        async def prev_page(interaction, button):
            if self.current_page > 0:
                self.current_page -= 1
                self._build_current_page()
                self._update_nav_states()
                await interaction.response.edit_message(view=self)
            else:
                await interaction.response.defer()
        
        @discord.ui.button(emoji="▶️", style=discord.ButtonStyle.secondary, row=1)
        async def next_page(interaction, button):
            if self.current_page < self.max_pages - 1:
                self.current_page += 1
                self._build_current_page()
                self._update_nav_states()
                await interaction.response.edit_message(view=self)
            else:
                await interaction.response.defer()
        
        prev_page.callback = prev_page
        next_page.callback = next_page
        
        self.add_item(prev_page)
        self.add_item(next_page)
        self._update_nav_states()
    
    def _update_nav_states(self):
        """Update navigation button states"""
        nav_buttons = [item for item in self.children if isinstance(item, discord.ui.Button)]
        if len(nav_buttons) >= 2:
            nav_buttons[0].disabled = (self.current_page == 0)  # Previous
            nav_buttons[1].disabled = (self.current_page >= self.max_pages - 1)  # Next
  • 🎯 Five Types: String, User, Role, Channel, Mentionable selects
  • 🔧 Smart Pagination: Automatic 25-option pagination with navigation
  • 📊 Multi-Selection: Configurable min/max value limits
  • 🎨 Dynamic Options: Runtime option generation and filtering
  • 🔄 Type Filtering: Channel type restrictions and validation
  • 🛡️ Large Datasets: Efficient handling of 100+ items
  • 📦 State Management: Page state preservation across interactions
  • 🎯 User Experience: Intuitive navigation with clear page indicators

📝 MODERN MODAL & FORMS MASTERY #

Professional Modal System (Red 3.5+) #

class AdvancedModal(discord.ui.Modal):
    """Feature-complete modal with validation and Red integration"""
    
    def __init__(self, title="Input Required", *, cog=None, author=None):
        super().__init__(title=title, timeout=300)
        self.cog = cog
        self.author = author
        self.result = None
        
    # Short text input with validation
    short_input = discord.ui.TextInput(
        label="Username",
        placeholder="Enter username (3-20 characters)...",
        min_length=3,
        max_length=20,
        required=True,
        row=0
    )
    
    # Long text input for detailed content
    long_input = discord.ui.TextInput(
        label="Description",
        placeholder="Enter detailed description...",
        style=discord.TextStyle.paragraph,
        min_length=10,
        max_length=1024,
        required=False,
        row=1
    )
    
    # Numeric input with validation
    number_input = discord.ui.TextInput(
        label="Amount",
        placeholder="Enter amount (1-1000)...",
        min_length=1,
        max_length=4,
        required=True,
        row=2
    )
    
    # URL input with format validation
    url_input = discord.ui.TextInput(
        label="Website URL",
        placeholder="https://example.com",
        min_length=7,
        max_length=200,
        required=False,
        row=3
    )
    
    # Configuration input with parsing
    config_input = discord.ui.TextInput(
        label="Configuration",
        placeholder="key1=value1\nkey2=value2",
        style=discord.TextStyle.paragraph,
        min_length=0,
        max_length=2000,
        required=False,
        row=4
    )
    
    async def on_submit(self, interaction: discord.Interaction):
        """Process form submission with comprehensive validation"""
        try:
            # Validate username
            username = self.short_input.value.strip()
            if not username.replace('_', '').replace('-', '').isalnum():
                raise ValueError("Username can only contain letters, numbers, _ and -")
            
            # Validate number
            try:
                amount = int(self.number_input.value)
                if not 1 <= amount <= 1000:
                    raise ValueError("Amount must be between 1 and 1000")
            except ValueError as e:
                if "invalid literal" in str(e):
                    raise ValueError("Amount must be a valid number")
                raise
            
            # Validate URL if provided
            url = self.url_input.value.strip()
            if url and not url.startswith(('http://', 'https://')):
                raise ValueError("URL must start with http:// or https://")
            
            # Parse configuration if provided
            config_data = {}
            if self.config_input.value.strip():
                try:
                    for line in self.config_input.value.strip().split('\n'):
                        if '=' in line:
                            key, value = line.split('=', 1)
                            config_data[key.strip()] = value.strip()
                except Exception:
                    raise ValueError("Configuration format: key=value (one per line)")
            
            # Build result
            self.result = {
                "username": username,
                "description": self.long_input.value.strip(),
                "amount": amount,
                "url": url if url else None,
                "config": config_data
            }
            
            # Success response
            embed = discord.Embed(
                title="✅ Form Submitted Successfully",
                color=0x00ff00,
                timestamp=discord.utils.utcnow()
            )
            embed.add_field(name="Username", value=username, inline=True)
            embed.add_field(name="Amount", value=str(amount), inline=True)
            if url:
                embed.add_field(name="URL", value=url, inline=False)
            
            await interaction.response.send_message(embed=embed, ephemeral=True)
            
            # Process with cog if available
            if self.cog and hasattr(self.cog, 'process_modal_data'):
                await self.cog.process_modal_data(interaction, self.result)
                
        except ValueError as e:
            # User-friendly error
            error_embed = discord.Embed(
                title="❌ Validation Error",
                description=str(e),
                color=0xff0000
            )
            await interaction.response.send_message(embed=error_embed, ephemeral=True)
            
        except Exception as e:
            # Unexpected error
            if self.cog:
                log.exception(f"Modal submission error: {e}")
            
            await interaction.response.send_message(
                "❌ An unexpected error occurred. Please try again.",
                ephemeral=True
            )
    
    async def on_error(self, interaction: discord.Interaction, error: Exception):
        """Handle modal errors gracefully"""
        log.exception(f"Modal error: {error}")
        
        if not interaction.response.is_done():
            await interaction.response.send_message(
                "❌ An error occurred while processing your submission.",
                ephemeral=True
            )

# Red Configuration Modal
class RedConfigModal(discord.ui.Modal):
    """Modal for Red-DiscordBot configuration management"""
    
    def __init__(self, cog, config_key, current_value="", *, title="Configuration"):
        super().__init__(title=title, timeout=300)
        self.cog = cog
        self.config_key = config_key
        
        self.config_input = discord.ui.TextInput(
            label=f"Set {config_key.replace('_', ' ').title()}",
            placeholder=f"Current: {current_value}" if current_value else "Enter new value...",
            default=str(current_value) if current_value else "",
            max_length=1000,
            required=True
        )
        self.add_item(self.config_input)
    
    async def on_submit(self, interaction: discord.Interaction):
        """Update Red configuration with type conversion"""
        try:
            new_value = self.config_input.value.strip()
            
            # Smart type conversion
            if self.config_key.endswith(('_enabled', '_enable')) or self.config_key.startswith('enable_'):
                new_value = new_value.lower() in ('true', '1', 'yes', 'on', 'enable')
            elif self.config_key.endswith(('_count', '_limit', '_max', '_min')):
                new_value = int(new_value)
            elif self.config_key.endswith(('_list', '_items')):
                new_value = [item.strip() for item in new_value.split(',') if item.strip()]
            elif self.config_key.endswith('_time'):
                new_value = float(new_value)
            
            # Update through Red's config system
            if interaction.guild:
                await self.cog.config.guild(interaction.guild).set_raw(self.config_key, value=new_value)
                scope = f"guild {interaction.guild.name}"
            else:
                await self.cog.config.set_raw(self.config_key, value=new_value)
                scope = "global"
            
            # Success response
            embed = discord.Embed(
                title="✅ Configuration Updated",
                description=f"Setting {self.config_key} updated for {scope}",
                color=0x00ff00
            )
            embed.add_field(name="New Value", value=f"`{new_value}`", inline=False)
            
            await interaction.response.send_message(embed=embed, ephemeral=True)
            
        except ValueError as e:
            await interaction.response.send_message(
                f"❌ Invalid Value: {e}",
                ephemeral=True
            )
        except Exception as e:
            log.exception(f"Config modal error: {e}")
            await interaction.response.send_message(
                "❌ Failed to update configuration. Check logs for details.",
                ephemeral=True
            )
  • 🎯 Five Inputs: Short text, long text, numeric, URL, config validation
  • 🔧 Validation: Client and server-side validation with clear errors
  • 📊 Red Integration: Native config system integration with type conversion
  • 🎨 Multi-Step: Progressive form workflows with state management
  • 🔄 Type Conversion: Automatic data type handling for Red configs
  • 🛡️ Error Handling: Comprehensive error catching and user feedback
  • 📦 Data Collection: Structured data collection and processing
  • 🎯 User Experience: Clear placeholders and validation messages

⚡ QUICK UI REFERENCE (Red 3.5+ Production) #

# Complete Modern UI Cog Template (Production-Ready)
from redbot.core import commands, Config
from redbot.core.utils.views import ConfirmView, SimpleMenu
import discord
import asyncio
import logging

log = logging.getLogger("red.mycog.ui")

# REQUIRED: GDPR compliance for UI data
__red_end_user_data_statement__ = (
    "This cog stores interaction data temporarily for UI state management. "
    "Data is automatically cleaned up after view timeouts."
)

class ProductionUICog(commands.Cog):
    """Production-ready UI cog with all modern patterns"""
    
    def __init__(self, bot):
        self.bot = bot
        self.config = Config.get_conf(self, identifier=1234567890, force_registration=True)
        self.active_views = {}
        
        # UI configuration
        self.config.register_guild(
            ui_timeout=300,
            ephemeral_by_default=True,
            theme_color=0x5865F2
        )
    
    async def cog_load(self):
        """Initialize UI system"""
        log.info("UI system loading...")
    
    async def cog_unload(self):
        """Clean up UI system"""
        log.info("UI system unloading...")
        for view in list(self.active_views.values()):
            view.stop()
        self.active_views.clear()
    
    async def red_delete_data_for_user(self, kwargs):
        """GDPR compliance - clean up user UI data"""
        user_id = kwargs.get("user_id")
        if user_id:
            for view in list(self.active_views.values()):
                if hasattr(view, 'author') and view.author.id == user_id:
                    view.stop()
    
    @commands.hybrid_command()
    async def ui_demo(self, ctx):
        """Demonstrate all UI components"""
        view = ComprehensiveUIDemo(ctx.author)
        embed = discord.Embed(
            title="🎨 UI Component Demo",
            description="Try all the different UI components below!",
            color=await ctx.embed_color()
        )
        view.message = await ctx.send(embed=embed, view=view)

# REQUIRED: Async setup function (Red 3.5+)
async def setup(bot):
    """Load the cog - MUST BE ASYNC in Red 3.5+"""
    cog = ProductionUICog(bot)
    await bot.add_cog(cog)

# Essential Component Limits (Discord.py 2.3+)
LIMITS = {
    "buttons_per_row": 5,
    "select_menus_per_row": 1, 
    "rows_per_view": 5,
    "total_components": 25,
    "select_options": 25,
    "modal_text_inputs": 5,
    "embed_fields": 25,
    "embed_total_chars": 6000,
    "view_timeout_max": 900  # 15 minutes
}

# Quick UI Patterns

# 1. Simple Confirmation
confirm = ConfirmView(ctx.author, timeout=30)
confirm.message = await ctx.send("Confirm action?", view=confirm)
await confirm.wait()
if confirm.result:  # True/False/None
    await ctx.send("✅ Confirmed!")

# 2. Paginated Menu  
pages = ["Page 1", "Page 2", "Page 3"]
menu = SimpleMenu(pages, timeout=180)
await menu.start(ctx)

# 3. Quick Modal
modal = AdvancedModal("Input Form")
await interaction.response.send_modal(modal)

# 4. Multi-Component View
view = CustomView(author=ctx.author)
view.add_item(discord.ui.Button(label="Action"))
view.add_item(discord.ui.Select(options=[...]))
await ctx.send("Interactive UI:", view=view)

# 5. Modern Button Types
@discord.ui.button(style=discord.ButtonStyle.primary)  # Blue
@discord.ui.button(style=discord.ButtonStyle.secondary)  # Gray
@discord.ui.button(style=discord.ButtonStyle.success)  # Green
@discord.ui.button(style=discord.ButtonStyle.danger)  # Red
@discord.ui.button(style=discord.ButtonStyle.link, url="...")  # Link

# 6. Modern Select Types (Discord.py 2.3+)
@discord.ui.select()  # String options
@discord.ui.user_select()  # User picker
@discord.ui.role_select()  # Role picker  
@discord.ui.channel_select()  # Channel picker
@discord.ui.mentionable_select()  # Users + Roles

# 7. Error Handling Pattern
async def on_error(self, interaction, error, item):
    log.exception(f"UI Error: {error}")
    if not interaction.response.is_done():
        await interaction.response.send_message(
            "❌ An error occurred.", ephemeral=True
        )

# 8. Security Pattern
async def interaction_check(self, interaction):
    if self.author and interaction.user != self.author:
        await interaction.response.send_message(
            "❌ Unauthorized access.", ephemeral=True
        )
        return False
    return True

# 9. Timeout Pattern
async def on_timeout(self):
    try:
        for child in self.children:
            child.disabled = True
        if self.message:
            await self.message.edit(view=self)
    except discord.HTTPException:
        pass

🎨 Master Red-DiscordBot’s complete modern UI component ecosystem for professional, secure, and scalable Discord bot interfaces with Red 3.5+ compliance and latest discord.py 2.3+ features!