Pycord is a modern, maintained fork of discord.py that continues to receive updates and support for new Discord features. This guide will help you migrate from discord.py to Pycord.
Why Migrate to Pycord?
Pycord offers several advantages over discord.py:
Active Development : Regular updates with new Discord features
Modern Discord Features : Slash commands, buttons, select menus, modals, and more
Community Support : Active Discord community and documentation
Backward Compatibility : Maintains compatibility with discord.py syntax
Full API Coverage : Supports the latest Discord API features
Installation
Uninstall discord.py
Install Pycord
Important: Uninstall discord.py before installing Pycord to avoid conflicts. Both libraries use the discord namespace.
Import Changes
Good news! Most imports remain the same:
# These imports work in both discord.py and Pycord
import discord
from discord.ext import commands, tasks
from discord import app_commands # For slash commands
No import changes are needed for basic functionality.
Python Version Support
# Requires Python 3.8+
Python 3.8 - 3.11 supported
Pycord 2.7+ requires Python 3.10 or higher. If you’re using Python 3.8 or 3.9, you’ll need to upgrade Python or use Pycord 2.6.
Bot Setup Comparison
The basic bot setup is nearly identical:
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot( command_prefix = "!" , intents = intents)
@bot.event
async def on_ready ():
print ( f "Logged in as { bot.user } " )
@bot.command ()
async def ping ( ctx ):
await ctx.send( "Pong!" )
bot.run( "TOKEN" )
As you can see, the code is identical! Your existing discord.py bot should work with minimal changes.
Slash Commands
Both libraries support slash commands, but with different syntax:
discord.py (app_commands)
Pycord (discord.Bot)
import discord
from discord import app_commands
from discord.ext import commands
bot = commands.Bot( command_prefix = "!" , intents = discord.Intents.default())
@bot.tree.command ( name = "hello" , description = "Say hello" )
async def hello ( interaction : discord.Interaction, name : str ):
await interaction.response.send_message( f "Hello { name } !" )
@bot.event
async def on_ready ():
await bot.tree.sync() # Sync commands
print ( f "Logged in as { bot.user } " )
bot.run( "TOKEN" )
Note: Pycord’s discord.Bot automatically syncs slash commands. You don’t need to manually call sync().
Slash Command Options
from discord import app_commands
import discord
@bot.tree.command ()
@app_commands.describe (
user = "The user to greet" ,
message = "Custom message"
)
async def greet (
interaction : discord.Interaction,
user : discord.User,
message : str = "Hello"
):
await interaction.response.send_message( f " { message } , { user.mention } !" )
UI Components
Both libraries support buttons, select menus, and modals with similar syntax:
import discord
from discord.ui import Button, View
class MyView ( View ):
@discord.ui.button ( label = "Click me!" , style = discord.ButtonStyle.primary)
async def button_callback ( self , interaction : discord.Interaction, button : Button):
await interaction.response.send_message( "Button clicked!" , ephemeral = True )
@bot.command ()
async def buttons ( ctx ):
view = MyView()
await ctx.send( "Click the button:" , view = view)
Minor Difference: Note the parameter order in button callbacks. Pycord typically uses (button, interaction) while discord.py uses (interaction, button).
import discord
from discord.ui import Select, View
class MyView ( View ):
@discord.ui.select (
placeholder = "Choose an option..." ,
options = [
discord.SelectOption( label = "Option 1" , value = "1" ),
discord.SelectOption( label = "Option 2" , value = "2" ),
]
)
async def select_callback ( self , interaction : discord.Interaction, select : Select):
await interaction.response.send_message( f "You selected: { select.values[ 0 ] } " )
Modals
import discord
from discord.ui import Modal, TextInput
class MyModal ( Modal , title = "Feedback Form" ):
feedback = TextInput( label = "Your feedback" , style = discord.TextStyle.paragraph)
async def on_submit ( self , interaction : discord.Interaction):
await interaction.response.send_message( f "Thanks for your feedback: { self .feedback.value } " )
@bot.tree.command ()
async def feedback ( interaction : discord.Interaction):
await interaction.response.send_modal(MyModal())
Difference: Pycord uses InputText instead of TextInput, and InputTextStyle instead of TextStyle.
Context Differences
Interaction Responses
# Responding to interactions
await interaction.response.send_message( "Hello!" )
await interaction.followup.send( "Follow-up message" )
# Deferring
await interaction.response.defer()
await interaction.followup.send( "Done!" )
Pycord provides ctx.respond() as a more intuitive alternative to interaction.response.send_message().
Cogs
Cog syntax is nearly identical:
from discord.ext import commands
from discord import app_commands
class MyCog ( commands . Cog ):
def __init__ ( self , bot ):
self .bot = bot
@commands.command ()
async def hello ( self , ctx ):
await ctx.send( "Hello from cog!" )
@app_commands.command ()
async def slash_hello ( self , interaction : discord.Interaction):
await interaction.response.send_message( "Hello from slash command!" )
async def setup ( bot ):
await bot.add_cog(MyCog(bot))
Renamed Classes and Methods
Emoji Changes
emoji = discord.Emoji( ... ) # Guild emoji
Bot Class
# Text commands
bot = commands.Bot( command_prefix = "!" )
# Slash commands
bot = commands.Bot( command_prefix = "!" , intents = intents)
# Use bot.tree for slash commands
Bridge Commands (Pycord Exclusive)
Pycord offers a unique feature: Bridge Commands that work as both text and slash commands:
from discord.ext import bridge
bot = bridge.Bot( command_prefix = "!" )
@bot.bridge_command ()
async def hello ( ctx , name : str ):
await ctx.respond( f "Hello { name } !" )
# Works as both:
# !hello World (text command)
# /hello World (slash command)
This feature doesn’t exist in discord.py!
Event Handling
Event handling is identical in both libraries:
@bot.event
async def on_ready ():
print ( f "Logged in as { bot.user } " )
@bot.event
async def on_message ( message ):
if message.author == bot.user:
return
if message.content.startswith( "hello" ):
await message.channel.send( "Hi!" )
await bot.process_commands(message) # Important for commands!
@bot.event
async def on_member_join ( member ):
await member.send( f "Welcome to { member.guild.name } !" )
Permissions and Intents
Both libraries handle intents the same way:
import discord
# Default intents
intents = discord.Intents.default()
# Enable specific intents
intents.message_content = True # Required for message commands
intents.members = True # Privileged intent
intents.presences = True # Privileged intent
# All intents (not recommended)
intents = discord.Intents.all()
bot = discord.Bot( intents = intents)
Voice Support
Voice functionality works identically:
@bot.command ()
async def join ( ctx ):
if ctx.author.voice:
channel = ctx.author.voice.channel
await channel.connect()
else :
await ctx.send( "You're not in a voice channel!" )
@bot.command ()
async def leave ( ctx ):
if ctx.voice_client:
await ctx.voice_client.disconnect()
Key Advantages of Pycord
1. Simpler Slash Commands
Pycord’s discord.Bot makes slash commands more intuitive than discord.py’s app_commands.CommandTree.
2. Bridge Commands
Unique to Pycord - support both text and slash commands with one decorator.
3. Active Development
Pycord receives regular updates with new Discord features:
Polls support
Voice messages
Forum channels
Soundboard support
And more!
4. Better Documentation
Pycord has dedicated documentation and an active community.
5. ctx.respond()
More intuitive than interaction.response.send_message().
Common Pitfalls
Common issues when migrating
Callback Parameter Order Pycord and discord.py sometimes use different parameter orders in UI callbacks: # Pycord
async def button_callback ( self , button , interaction ):
pass
# discord.py
async def button_callback ( self , interaction , button ):
pass
InputText vs TextInput Pycord uses InputText, discord.py uses TextInput: # Pycord
from discord.ui import InputText
# discord.py
from discord.ui import TextInput
Sync() Not Needed Pycord automatically syncs slash commands. Don’t call bot.tree.sync(). Python Version Make sure you’re using Python 3.10+ for Pycord 2.7+.
Migration Checklist
Click to expand migration checklist
Side-by-Side Example
Here’s a complete bot example in both libraries:
import discord
from discord import app_commands
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot( command_prefix = "!" , intents = intents)
@bot.event
async def on_ready ():
await bot.tree.sync()
print ( f "Logged in as { bot.user } " )
@bot.command ()
async def ping ( ctx ):
await ctx.send( "Pong!" )
@bot.tree.command ( name = "hello" )
async def hello ( interaction : discord.Interaction, name : str ):
await interaction.response.send_message( f "Hello { name } !" )
bot.run( "TOKEN" )
Getting Help
If you need help migrating:
Welcome to the Pycord community!