DiscordPYComponents
Discord Components V2 for discord.py — a lightweight helper library for building rich, interactive messages with the modern Components V2 system.
Requires the
IS_COMPONENTS_V2flag (1 << 15).
When this flag is set, traditionalcontentandembedsare disabled; all content must be sent as components.
PyPI: https://pypi.org/project/DiscordPYComponents/
Official Discord docs:
Installation
pip install DiscordPYComponents
Requirements
- Python 3.10+
- discord.py (or any library that gives you a bot token + channel/interaction)
- aiohttp
Quick start
from py_components import (
UI, TextDisplay, Button, ButtonStyle, Container, Separator, Section, Thumbnail
)
@bot.command()
async def menu(ctx):
ui = UI(
TextDisplay("# Welcome"),
TextDisplay("Choose an option below:"),
Button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS),
Button("Cancel", custom_id="cancel", style=ButtonStyle.DANGER),
)
await ui.send(ctx)
Component types
Content components
| Class | Type | Description |
|---|---|---|
TextDisplay / Text |
10 | Markdown text (1–4000 chars) |
Thumbnail |
11 | Small image (Section accessory only) |
MediaGallery |
12 | 1–10 images / media items |
File |
13 | Displays an uploaded attachment |
Layout components
| Class | Type | Description |
|---|---|---|
ActionRow |
1 | Holds up to 5 Buttons or one Select menu |
Section |
9 | 1–3 TextDisplays + optional accessory (Button or Thumbnail) |
Separator |
14 | Vertical padding / visual divider |
Container |
17 | Groups children with optional accent colour bar |
Interactive components
| Class | Type | Description |
|---|---|---|
Button |
2 | Clickable button |
StringSelect |
3 | Dropdown of string options |
UserSelect |
5 | Select users |
RoleSelect |
6 | Select roles |
MentionableSelect |
7 | Select users or roles |
ChannelSelect |
8 | Select channels |
Button styles
| Style | Value | Colour / behaviour |
|---|---|---|
ButtonStyle.PRIMARY |
1 | Blurple |
ButtonStyle.SECONDARY |
2 | Grey |
ButtonStyle.SUCCESS |
3 | Green |
ButtonStyle.DANGER |
4 | Red |
ButtonStyle.LINK |
5 | Grey – opens a URL (requires url=) |
ButtonStyle.PREMIUM |
6 | Blurple – requires sku_id= |
Button("Click me", custom_id="btn1", style=ButtonStyle.PRIMARY)
Button("Docs", style=ButtonStyle.LINK, url="https://discord.com/developers/docs")
Button("Buy", style=ButtonStyle.PREMIUM, sku_id="1234567890")
Building a rich layout
from py_components import (
UI, Container, TextDisplay, Section, Thumbnail,
Separator, SeparatorSpacing, MediaGallery, Button, ButtonStyle
)
ui = UI(
Container(
TextDisplay("# Server Status"),
Separator(divider=True, spacing=SeparatorSpacing.SMALL),
Section(
TextDisplay("**Online** — 1 204 members"),
TextDisplay("Peak today: 312"),
accessory=Thumbnail("https://cdn.discordapp.com/icons/.../icon.png"),
),
Separator(),
MediaGallery(
"https://example.com/chart1.png",
"https://example.com/chart2.png",
),
accent_color=0x5865F2, # Discord blurple
),
Button("Refresh", custom_id="refresh", style=ButtonStyle.PRIMARY),
)
await ui.send(ctx)
Top-level components can be mixed freely. The UI class automatically wraps loose Buttons into ActionRows (max 5 per row). Place select menus inside an explicit ActionRow yourself:
from py_components import ActionRow, StringSelect, SelectOption
ui = UI(
TextDisplay("Pick a colour:"),
ActionRow(
StringSelect(
custom_id="colour",
options=[
SelectOption("Red", "red"),
SelectOption("Green", "green"),
SelectOption("Blue", "blue"),
],
placeholder="Choose…",
)
),
)
View & callbacks
from py_components import View, ButtonStyle
view = View()
async def on_confirm(interaction):
await interaction.response.send_message("Confirmed!", ephemeral=True)
view.add_button("Confirm", custom_id="confirm", style=ButtonStyle.SUCCESS, callback=on_confirm)
view.add_button("Cancel", custom_id="cancel", style=ButtonStyle.SECONDARY)
# Send the payload yourself or combine with UI
payload = view.to_payload()
You can also use the decorator helper:
from py_components import button, View
@button("Click Me", custom_id="my_btn")
async def on_click(interaction):
await interaction.response.send_message("Clicked!")
Global registry
from py_components import default_registry
@default_registry.route("confirm")
async def handle_confirm(interaction):
await interaction.response.send_message("OK")
# Later, in your interaction listener:
await default_registry.dispatch(interaction.data["custom_id"], interaction)
File attachments
Upload the file with the message and reference it with the File component:
from py_components import UI, File, TextDisplay
ui = UI(
TextDisplay("Here is the report:"),
File("report.pdf"), # becomes attachment://report.pdf
)
# When calling the raw HTTP helper you would also pass the file bytes;
# with discord.py you can combine the payload with files=...
await ui.send(ctx)
Responding to interactions
@bot.event
async def on_interaction(interaction):
if interaction.type.name == "component":
custom_id = interaction.data.get("custom_id")
# … route to your callbacks …
# or reply with a new V2 message:
ui = UI(TextDisplay(f"You pressed `{custom_id}`"))
await ui.reply(interaction, ephemeral=True)
Payload shape
Every payload produced by this library looks like:
{
"flags": 32768,
"components": [
{ "type": 10, "content": "Hello" },
{
"type": 1,
"components": [
{ "type": 2, "style": 1, "label": "OK", "custom_id": "ok" }
]
}
]
}
You can also obtain the dict with ui.to_payload() / view.to_payload() and send it through any HTTP client or discord.py’s low-level methods.
Limitations (Discord-side)
- Once a message is sent with
IS_COMPONENTS_V2the flag cannot be removed. content,embeds,stickersandpollare disabled on V2 messages.- Attachments only appear when referenced by a
Fileor media component. - Max 40 components per message.
- Buttons must live in an ActionRow (or as a Section accessory).
- Only one select menu per ActionRow.
License
MIT
Metadata
Release files for DiscordPYComponents 1.0.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.0.0.tar.gz | 10.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| discordpycomponents-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.6 kB
Release files / discordpycomponents-1.0.0.tar.gz
| Download URL | discordpycomponents-1.0.0.tar.gz |
|---|---|
| Size | 10.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c9b6f9b0b885ad2c39fb7f00d3782fc6bec4d93c55e09523e56c750e6b9d0851
|
|
BLAKE2b-256 checksum How to use checksums |
c798488e061c8dbdb6f5aa65bb30b03bfa9cb5a21a310e8addd0ad247c84e01d
|
| 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.0.0-py3-none-any.whl
| Download URL | discordpycomponents-1.0.0-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
878fe0c2d8766ba30823fe747a75cf42f2b81d13a4f35573f20fa899dc399193
|
|
BLAKE2b-256 checksum How to use checksums |
1b0d02eac9c6b90459854184ac67c5dafbdd4f4363dc22a82b7a59c127963e96
|
| 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