Buttons & UI Extensions
Add buttons to Discord's chat bar, message popover, and header bar using Plexcord's UI extension APIs.
Buttons & UI Extensions
Plexcord lets you add custom buttons to several locations in Discord's interface:
- Chat Bar: The row of buttons next to the message input box
- Message Popover: The toolbar that appears when hovering a message
- Header Bar: The icons in the top-right of a channel
Chat Bar Buttons
Add a button next to the message input (alongside the emoji, GIF, and gift buttons).
Setup
import { addChatBarButton, removeChatBarButton, ChatBarButton } from "@api/ChatButtons";
import { Tooltip } from "@webpack/common";
// Define the button component
const MyChatButton: ChatBarButton = ({ isMainChat }) => {
// isMainChat is false in thread sidebars, search results, etc.
if (!isMainChat) return null;
return (
<Tooltip text="My Custom Action">
{({ onMouseEnter, onMouseLeave }) => (
<button
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={() => {
console.log("Chat bar button clicked!");
}}
style={{
background: "none",
border: "none",
cursor: "pointer",
padding: "0 4px",
color: "var(--interactive-normal)"
}}
>
🎯
</button>
)}
</Tooltip>
);
};
export default definePlugin({
name: "ChatBarExample",
description: "Adds a button to the chat bar",
authors: [{ name: "You", id: 0n }],
start() {
addChatBarButton("my-chat-button", MyChatButton);
},
stop() {
removeChatBarButton("my-chat-button");
}
});Using Discord's Button Styles
For a consistent look that matches Discord's own chat bar buttons, use the Button component:
import { Button, Tooltip } from "@webpack/common";
import { SquarePen } from "lucide-react";
const MyChatButton: ChatBarButton = ({ isMainChat }) => {
if (!isMainChat) return null;
return (
<Tooltip text="Format Text">
{(tooltipProps) => (
<Button
{...tooltipProps}
size={Button.Sizes.MIN}
look={Button.Looks.BLANK}
onClick={handleClick}
style={{ padding: "0 6px" }}
>
<SquarePen size={20} />
</Button>
)}
</Tooltip>
);
};Accessing the Message Input
To read or modify the message input content when your button is clicked:
import { ComponentDispatch } from "@webpack/common";
import { findByProps } from "@webpack";
const MyChatButton: ChatBarButton = ({ isMainChat }) => {
if (!isMainChat) return null;
function onClick() {
// Get the draft content
const DraftStore = findByProps("getDraft");
// Channel ID can be accessed from the channel store
// ... implementation varies
console.log("Text input accessed");
}
return (
<button onClick={onClick}>📝</button>
);
};Message Popover Buttons
Add buttons to the hover toolbar that appears on messages (the row with 👍, ✉️, ⋯).
Setup
import { addMessagePopoverButton, removeMessagePopoverButton } from "@api/MessagePopover";
export default definePlugin({
name: "MessagePopoverExample",
description: "Adds button to message hover toolbar",
authors: [{ name: "You", id: 0n }],
start() {
addMessagePopoverButton("my-popover-button", message => {
// Return null to hide button for specific messages
if (!message.content) return null;
return {
label: "Copy Text",
icon: () => <span>📋</span>,
message,
channel: { id: message.channel_id },
onClick() {
navigator.clipboard.writeText(message.content);
}
};
});
},
stop() {
removeMessagePopoverButton("my-popover-button");
}
});Header Bar Buttons
Add icon buttons to the top-right area of a channel (alongside the search, inbox, and members list buttons).
Setup
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
// Or for header bar specifically, use patches to inject into the toolbar component
// For most use cases, header bar injection requires patching
// the ChannelHeader component:
import { addDecoration } from "@api/MessageDecorations";
// Example: Channel header button via patch
patches: [
{
find: "toolbar:function",
replacement: {
match: /(?<=toolbar:function.{0,100})\[/,
replace: "[Vencord.Plugins.plugins.MyPlugin.renderHeaderButton(), "
}
}
],
renderHeaderButton() {
return (
<Tooltip key="my-header-btn" text="My Feature">
{(props) => (
<button {...props} onClick={() => this.toggleMyFeature()}>
🔧
</button>
)}
</Tooltip>
);
}Header bar injection typically requires patches since there's no dedicated API. Use the chat bar when possible; it has a clean API.
Server List Icons
Add icons to Discord's left server list:
import {
addServerListElement,
removeServerListElement,
ServerListRenderPosition
} from "@api/ServerList";
function MyServerListIcon() {
return (
<Tooltip text="My Plugin">
{(props) => (
<div
{...props}
onClick={() => doSomething()}
style={{
width: "48px",
height: "48px",
borderRadius: "50%",
background: "var(--brand-experiment)",
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
fontSize: "20px"
}}
>
🎯
</div>
)}
</Tooltip>
);
}
export default definePlugin({
name: "ServerListExample",
description: "Adds icon to server list",
authors: [{ name: "You", id: 0n }],
start() {
addServerListElement(
ServerListRenderPosition.Above, // Above or Below server icons
MyServerListIcon
);
},
stop() {
removeServerListElement(
ServerListRenderPosition.Above,
MyServerListIcon
);
}
});Complete Example: Word Count Button
Adds a chat bar button that shows word count of what you've typed:
import definePlugin from "@utils/types";
import { addChatBarButton, removeChatBarButton, ChatBarButton } from "@api/ChatButtons";
import { Tooltip } from "@webpack/common";
import { DraftStore, SelectedChannelStore } from "@webpack/common";
const WordCountButton: ChatBarButton = ({ isMainChat }) => {
if (!isMainChat) return null;
const [count, setCount] = React.useState(0);
React.useEffect(() => {
const update = () => {
const channelId = SelectedChannelStore.getChannelId();
const draft = DraftStore.getDraft(channelId, 0) ?? "";
const words = draft.trim().split(/\s+/).filter(Boolean).length;
setCount(words);
};
const interval = setInterval(update, 500);
return () => clearInterval(interval);
}, []);
if (count === 0) return null;
return (
<Tooltip text={`${count} word${count !== 1 ? "s" : ""}`}>
{({ onMouseEnter, onMouseLeave }) => (
<span
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
style={{
color: "var(--text-muted)",
fontSize: "12px",
padding: "0 6px",
alignSelf: "center"
}}
>
{count}w
</span>
)}
</Tooltip>
);
};
export default definePlugin({
name: "WordCount",
description: "Shows word count in chat bar while typing",
authors: [{ name: "You", id: 0n }],
start() {
addChatBarButton("word-count", WordCountButton);
},
stop() {
removeChatBarButton("word-count");
}
});