Custom Commands docsCustom CommandsDocs

Search docs

Find a page, function or guide

HomeSupportDashboard

Getting started

  • Introduction
  • Your first command
  • How a command runs
  • Variables in replies
  • Text commands
  • Events
  • Using Slushy

Guides

  • Welcome messages
  • Role menus
  • Auto moderation
  • Social alerts
  • Moderation commands
  • Levels and XP
  • Economy and coins
  • Ticket system
  • Giveaways
  • Poll
  • Button game
  • Reminders

Concepts

  • Buttons and menus
  • Forms
  • Saving data
  • Permissions and cooldowns
  • Limits and premium
  • Common mistakes

API reference

  • Response
  • Messages
  • Embeds
  • Members and roles
  • Channels
  • Storage
  • Checks
  • Views
  • Components
  • Modals
  • Interactions
  • Paginators
  • Web and files
  • Scheduling
  • Server events and polls
  • Time
  • Waiting
  • Utilities
  • Objects
  • Event variables

Examples

  • /8ball
  • /coinflip
  • /avatar
  • /serverinfo
  • /rank
  • /remind

API reference

View .md

Views

Buttons and menus whose clicks are handled inside the same command. The best choice for games, confirmations and menus.

A View is a set of buttons and menus. When someone clicks, a function in the same command runs. That function can see every variable of the command.

Use a View when the clicks happen while the command is running: a "Yes / No" question, a game, a page of choices. Use a component handler only for buttons that must keep working for days, like a role panel.

Read Buttons and menus to choose between the two.

A full example#

python
# pre-hook
class Confirm(View):
    def __init__(self, user_id):
        self.user_id = user_id      # only this member can click
        self.choice = None

    @button(label="Yes", style="success", custom_id="yes")
    async def yes(self, interaction, component):
        await defer(interaction, action="update")
        self.choice = "yes"
        self.disable_all_components()
        await self.update_view()
        self.stop()

    @button(label="No", style="danger", custom_id="no")
    async def no(self, interaction, component):
        await defer(interaction, action="update")
        self.choice = "no"
        self.disable_all_components()
        await self.update_view()
        self.stop()

view = Confirm(user_id=user.id)
view.interaction = interaction      # lets update_view() change the reply
set_view(view)
set_content("Delete all your saved notes?")
python
# post-hook
if view.choice == "yes":
    send_message(channel, "Deleted.")
elif view.choice is None:
    send_message(channel, "No answer. Nothing was deleted.")

What happens:

  1. The pre-hook makes the View and calls set_view.
  2. The bot sends the reply with the buttons and waits for clicks.
  3. A click runs yes or no. self.stop() ends the View.
  4. The post-hook runs. view.choice holds the answer.

Rules for Views

  • Never call super().__init__() in your View.
  • After set_view(view), do not call view.wait(). The bot does it.
  • Callbacks always look like async def name(self, interaction, component).

View attributes#

Set these on your View.

AttributeWhat it does
user_idA member ID, or a list of IDs, allowed to click. None lets anyone click.
interactionSet to interaction so update_view() changes the command's reply.
messageSet when the View is on a message you sent. send_message sets it for you.
guild_idSet for you by set_view and send_message.

View methods#

self.stop#

Ends the View. wait() then returns "stopped".

python
self.stop()

stop() also ends the callback. Lines after it do not run, so call it last.

self.update_view#

Shows your changes to the buttons on the message. Needs await.

python
await self.update_view()

self.update_component#

Changes one button or menu. Use the custom_id you wrote in the decorator.

python
self.update_component(custom_id, **changes)
python
self.count += 1
self.update_component("add", label=f"Clicked {self.count} times")
await self.update_view()

self.disable_all_components#

Greys out every button and menu. Pass ignore=[...] to keep some active.

python
self.disable_all_components(ignore=None)

view.wait#

Waits for clicks until the View stops or nobody clicks for timeout seconds. Needs await.

python
await view.wait(timeout=180, disable_on_timeout=False, ignore_errors=False)
ArgumentWhat it does
timeoutSeconds with no clicks before the View ends. Each click starts the clock again.
disable_on_timeoutGrey out the buttons when time runs out.
ignore_errorsKeep the View going when a callback has an error.

Returns "stopped" or "timeout".

Only call wait yourself when you did not use set_view, for example when the View is on a message from send_message:

python
view = Poll()
send_message(channel, "Vote now!", components=view)
result = await view.wait(timeout=60, disable_on_timeout=True)

button#

Adds a button. Put it above an async def inside your View.

python
@button(
    label,
    custom_id=None,
    style="primary",
    is_disabled=False,
    emoji=...,
    row=1,
    url=None,
    hidden=False,
)
ArgumentWhat it does
labelThe button text.
custom_idA name for the button. Needed for update_component.
styleprimary (blue), secondary (grey), success (green), danger (red), link.
emojiAn emoji shown on the button.
rowWhich row, from 1 to 5.
urlMakes it a link button. Link buttons do not run a callback.
hiddenTrue hides the button until you show it with update_component.

A wrong style silently becomes primary.

In signatures on this page, ... means the argument is optional and has no default. Leave it out when you do not need it.

Extra keyword arguments are kept in component.metadata, so a callback can read them.

text_menu#

Adds a menu with your own options.

python
@text_menu(
    options,
    custom_id=None,
    placeholder=...,
    min_values=0,
    max_values=1,
    is_disabled=False,
    row=None,
    hidden=False,
)

options is a list of TextOption:

python
TextOption(label, value, description=..., emoji=..., default=False)

What the member picked is in interaction.values. It is always a list, even with max_values=1.

python
class Colors(View):
    @text_menu(
        options=[
            TextOption(label="Red", value="red", emoji="🔴"),
            TextOption(label="Blue", value="blue", emoji="🔵"),
        ],
        custom_id="color",
        placeholder="Pick a color",
    )
    async def pick(self, interaction, component):
        await defer(interaction, action="update")
        self.color = interaction.values[0]
        self.stop()

Watch out

Use interaction.values, not component.values. component is what you declared, not what the member picked.

role_menu#

Adds a menu that lists the server's roles. interaction.values holds the picked role IDs.

python
@role_menu(custom_id=None, placeholder=..., min_values=0, max_values=1,
           is_disabled=False, row=None, hidden=False)

user_menu#

Adds a menu that lists members. interaction.values holds the picked member IDs. Same arguments as role_menu.

python
@user_menu(custom_id=None, placeholder=..., min_values=0, max_values=1)

channel_menu#

Adds a menu that lists channels. interaction.values holds the picked channel IDs.

python
@channel_menu(channel_types, custom_id=None, placeholder=...,
              min_values=0, max_values=1)

channel_types is a list from: text, voice, category, news, news-thread, public-thread, private-thread, forum, stage.

Layout rules#

Discord limits how buttons and menus fit on a message:

  • At most 5 rows.
  • At most 5 buttons in a row.
  • A menu takes a whole row. It cannot share a row with buttons or another menu.

The bot stops with a clear message when a View breaks a rule.

Answering a click#

Discord wants an answer to every click within 3 seconds. Start each callback with one of these:

CallUse when
await defer(interaction, action="update")You change the message the buttons are on.
await defer(interaction, ephemeral=True)You send a new private message later.
await send_modal(form, interaction)You open a form. Opening a form is the answer. Do not defer too.

See Interactions and Modals.

PreviousChecksNext Components

On this page

  • A full example
  • View attributes
  • View methods
  • self.stop
  • self.update_view
  • self.update_component
  • self.disable_all_components
  • view.wait
  • button
  • text_menu
  • role_menu
  • user_menu
  • channel_menu
  • Layout rules
  • Answering a click
Custom CommandsCustom Commands

The #1 Custom Commands Discord bot — build commands, events and databases with zero boilerplate.

Join the support server

Product

  • Overview
  • Why us?
  • Premium
  • Template shop

Features

  • AI Agent
  • Reaction roles
  • Welcome messages
  • Embed builder
  • Social alerts
  • Custom bot

Resources

  • Docs
  • Blog
  • Support
  • Dashboard

Company

  • Privacy
  • Terms
  • Contact
contact@ccbot.app

2 Frederick Street, London, WC1X 0ND

Payments secured by Stripe

© WEiRDSOFT LTD. All rights reserved.

Custom Commands