Why patch?
Discord’s UI is built from React components you don’t own. To add a button to a menu, hide an element, or wrap a screen in your own, you patch the component: intercept it as it renders and adjust the output. Patching is the core technique behind most visual plugins. You patch with a patcher: an object that wraps a target’s method (including a component’s render) with your own logic, and can revert every patch it made in one call. If you’re new to it, read Function Patching first; this page applies the same patcher to React components.Everything your plugin shows on screen reaches the UI by patching a component Discord already renders. You return modified elements from inside Discord’s own render, so your UI sits in Discord’s real tree and has access to its context.
The three patch types
A patch hooks a function at one of three points:- after
- before
- instead
Runs after the original and receives its
result. Return a new value to replace what it produced, the usual choice for editing a component’s rendered output.Patching a component’s render
A React component renders through itstype (or render) function. Find the module that owns the component, then patch that function with after and return your modified tree.
findByProps and the other findBy* helpers cache their results, so finding the same component a second time is cheap. The searches that stay slow are ones with a manual filter function, which can’t be cached by default. If you need one, wrap it with createCacheable so its result is remembered after the first cold start.Component kinds
Where you point a patch depends on what kind of component it is. The render function lives in a different place for each.- memo
- class
- function
A
React.memo component wraps the real component and stores it on .type. Patch .type, not the memo wrapper itself.Reaching into a render
Many components are never returned by a Metro search. They are defined inside another component’s module and only exist as elements inside its rendered output. To patch one of these, patch a component you can find, then walk itsresult to reach the child.
The result of a render is a tree of React elements. Each element’s component is on its type, its props are on props, and its rendered children are on props.children. Walk that tree to find the element you want.
Write tree patches to coexist with other plugins. Your plugin won’t be the only one patching a given component, so:
- Guard every tree lookup with optional chaining and a null check.
findInTreereturnsnullwhen Discord’s structure shifts, and another plugin may have already reshaped the tree you’re walking. - Add to
children, don’t replace it. When you insert an element, push onto the existing children array (or makechildrenan array if it isn’t one) instead of overwriting it, so an earlier plugin’s additions survive.
Temporary patches from a parent
Sometimes the child you want isn’t an element you can edit in place, but a component whose own render you need to patch, and it has no stable reference Metro can find. The component is created fresh on the parent’s render, so a patch you leave on it would target a reference that is gone on the next render. The pattern: patch the stable parent, and from inside that patch apply a one-shot patch to the child for that render only. Every patch call returns an unpatch function, andpossess accepts a { once: true } option that reverts the patch automatically after it fires once.
{ once: true } is what keeps this safe: the child reference is new on every parent render, so the patch must not outlive the render that created it. If you need finer control, capture the unpatch function each patch call returns and call it yourself.Cleaning up
Every patcher tracks the patches it applied. CallunpatchAll in stop and the component reverts to Discord’s original render: no leftover wrappers, no double-patching when the plugin is re-enabled.
Unpatching only restores the original function. Components that already rendered with your patch keep showing the patched output until React renders them again, so a screen that’s currently mounted won’t update on its own. After you unpatch on shutdown, force the affected components to re-render so the original UI comes back without the user navigating away and returning.
How you force that re-render depends on the kind of component you patched.
- class
- function
A class component instance has a built-in
forceUpdate method. If your patch captured the instance through this, call self.forceUpdate() to re-render it.