Skip to main content

DiscordPYComponents

Discord Components V2 helper for discord.py.

Needs the IS_COMPONENTS_V2 flag (1 << 15). That turns off normal content/embeds — everything goes through components.

pip install DiscordPYComponents

Python 3.10+, discord.py, aiohttp.

Quick start

from py_components import UI, TextDisplay, Button, ButtonStyle

@bot.command()
async def menu(ctx):
    ui = UI(
        TextDisplay("pick one"),
        Button("yes", custom_id="yes", style=ButtonStyle.SUCCESS),
        Button("no", custom_id="no", style=ButtonStyle.DANGER),
    )
    await ui.send(ctx)

Components

Content

class type what
TextDisplay / Text 10 markdown text
Thumbnail 11 small image (section accessory)
MediaGallery 12 1-10 images
File 13 attachment:// file

Layout

class type what
ActionRow 1 up to 5 buttons or one select
Section 9 text + button/thumbnail accessory
Separator 14 spacing / divider
Container 17 groups stuff, optional accent colour

Interactive

class type
Button 2
StringSelect 3
UserSelect 5
RoleSelect 6
MentionableSelect 7
ChannelSelect 8

Buttons

ButtonStyle.PRIMARY    # 1 blurple
ButtonStyle.SECONDARY  # 2 grey
ButtonStyle.SUCCESS    # 3 green
ButtonStyle.DANGER     # 4 red
ButtonStyle.LINK       # 5 needs url=
ButtonStyle.PREMIUM    # 6 needs sku_id=
Button("click", custom_id="x", style=ButtonStyle.PRIMARY, emoji="🔥")
Button("docs", style=ButtonStyle.LINK, url="https://discord.com/developers/docs")

Layout example

from py_components import (
    UI, Container, TextDisplay, Section, Thumbnail,
    Separator, MediaGallery, Button, ButtonStyle
)

ui = UI(
    Container(
        TextDisplay("# status"),
        Separator(),
        Section(
            TextDisplay("**online** — 1204 members"),
            accessory=Thumbnail("https://cdn.discordapp.com/icons/.../icon.png"),
        ),
        MediaGallery("https://example.com/a.png", "https://example.com/b.png"),
        accent_color=0x5865F2,
    ),
    Button("refresh", custom_id="refresh"),
)
await ui.send(ctx)

Selects go in an ActionRow:

from py_components import ActionRow, StringSelect, SelectOption

ui = UI(
    TextDisplay("colour?"),
    ActionRow(
        StringSelect(
            "colour",
            options=[
                SelectOption("red", "red"),
                SelectOption("blue", "blue"),
            ],
            placeholder="pick one",
        )
    ),
)

Sending

await ui.send(ctx)                          # channel
await ui.reply(interaction)                 # interaction response / followup
await ui.reply(interaction, ephemeral=True)
await ui.edit(interaction)                  # edit original / component message

Uses discord.py methods when it can, raw HTTP otherwise.

Component ids

Ids get assigned automatically when you build a payload. Set your own if you need stable ones:

TextDisplay("hi", id=10)
Button("x", custom_id="x", id=11)
from py_components import find_by_id, replace_by_id, assign_ids

payload = ui.to_payload()
node = ui.get(10)                           # by id
payload = ui.replace(10, TextDisplay("updated", id=10))

Handy when editing a message and only swapping one piece.

Modals

from py_components import (
    Modal, Label, TextInput, TextInputStyle,
    RadioGroup, RadioGroupOption, Checkbox, FileUpload
)

modal = Modal(
    "report",
    "bug report",
    Label("what happened", TextInput("desc", style=TextInputStyle.PARAGRAPH)),
    Label("severity", RadioGroup("sev", [
        RadioGroupOption("low", "low"),
        RadioGroupOption("high", "high"),
    ])),
    Label("i can reproduce this", Checkbox("ok")),
    Label("screenshots", FileUpload("shots", min_values=0, required=False)),
)

@bot.tree.command(name="report")
async def report(interaction):
    await modal.open(interaction)
class type notes
Label 18 wraps one control, label ≤45
TextInput 4 short / paragraph
FileUpload 19 0-10 files
RadioGroup 21 one choice, 2-10 options
CheckboxGroup 22 multi, 1-10 options
Checkbox 23 single yes/no

Max 5 top-level components per modal.

Callbacks

from py_components import View, ButtonStyle

view = View()

async def on_yes(interaction):
    await interaction.response.send_message("ok", ephemeral=True)

view.add_button("yes", "yes", style=ButtonStyle.SUCCESS, callback=on_yes)
payload = view.to_payload()
from py_components import default_registry

@default_registry.route("yes")
async def handle_yes(interaction):
    await interaction.response.send_message("ok")

await default_registry.dispatch(interaction.data["custom_id"], interaction)

Emoji / defaults

from py_components import make_emoji, UserSelect

Button("pepe", custom_id="p", emoji=make_emoji("pepe", id=123, animated=True))
Button("ok", custom_id="ok", emoji="✅")

UserSelect(
    "mods",
    default_values=[{"id": "111", "type": "user"}],
    min_values=1,
    max_values=3,
)

Limits (discord side)

  • V2 flag can't be removed once set
  • no content / embeds / stickers / poll on V2 messages
  • attachments only show if referenced by a File / media component
  • 40 components max per message
  • buttons need an ActionRow (or section accessory)
  • one select per ActionRow

License

MIT

Metadata

Release files for DiscordPYComponents 1.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for DiscordPYComponents 1.3.0
File Size Uploaded
discordpycomponents-1.3.0.tar.gz 12.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for DiscordPYComponents 1.3.0
File Interpreter ABI Platform
discordpycomponents-1.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.3 kB

Release files / discordpycomponents-1.3.0.tar.gz

Download URL discordpycomponents-1.3.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
808b0720bc177e647694ac689d88602fa39815739178915f3cac8cf9edcddf0a
BLAKE2b-256 checksum
How to use checksums
a5c2a12e5882c5ad958ab114c90f63fcaf09fe1204b3c52f44f0b3eb79436d03
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release files / discordpycomponents-1.3.0-py3-none-any.whl

Download URL discordpycomponents-1.3.0-py3-none-any.whl
Size 14.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
339285342227b3268f41bf07a000a4458568d600f3ede2ea1b22132eabcd897e
BLAKE2b-256 checksum
How to use checksums
9727417cf9384e8fd40be97be2fd0954ac5c41fe5bb10c7ddc419a77865cce21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release history Release notifications | RSS feed

1.3.2

2 release files

1.3.1

2 release files

This release

1.3.0 This release

2 release files

1.0.0

2 release files

0.2.33

2 release files

0.2.31

2 release files

0.2.23

2 release files

0.2.11

2 release files

0.2.3

2 release files

0.2.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page