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)
| File | Size | Uploaded | |
|---|---|---|---|
| discordpycomponents-1.3.0.tar.gz | 12.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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