Skip to main content

What are components?

metro.components holds ready-made handles to the Discord modules that render UI. Use them to build settings panels, screens, and anything else your addon draws, so it matches Discord’s own look. Each handle is found through Metro. Most are lazy: the search runs the first time you read a property, so importing them costs nothing at startup.

Available handles

Only Discord and Switch are typed. The rest are typed any, so check their props against Discord’s build before you rely on them.
Switch is the only handle searched for eagerly, when Unbound loads. Every other handle is a lazy proxy that resolves on first property access.

The Discord design system

Build most addon UI from Discord. It is Discord’s mobile design system: a single module that holds its components, style factory, alert helpers, and color utilities.
The typed members, by group:
  • Text and Heading take a variant (such as 'text-md/semibold' or 'heading-lg/bold'), a color, lineClamp, and the usual view props.
  • TextStyleSheet maps each variant name to its TextStyle, for when you need the style object instead of a component.
  • TableRowGroup is the rounded container for a list of rows. It takes title, description, and helperText.
  • TableRow is the basic row: label, subLabel, icon, trailing, arrow, variant, and onPress. It has three statics: TableRow.Icon, TableRow.Arrow, and TableRow.TrailingText.
  • TableSwitchRow, TableCheckboxRow, and TableRadioRow are rows with a built-in control.
  • TableRowIcon renders an icon from an asset id (source) or a component (IconComponent).
  • TableRowGroupTitle, RowButton, and RowGroup cover headings and button rows.
  • Button takes text, variant, size, icon, loading, disabled, and onPress. It has a Button.Icon static.
  • IconButton, ImageButton, and FloatingActionButton are the icon-only variants.
  • TextField, TextArea, TextAreaField, SearchField, GhostInput, and SplitTextInput are the text field family. They share size, status, isClearable, and leading and trailing icons or text.
  • TextInput is Discord’s own form input, not React Native’s. It takes title, helpText, and error.
  • Slider, Checkbox, and SegmentedControl cover the remaining controls. SegmentedControl is driven by a state object.
  • Stack spaces its children along a direction with a given spacing.
  • Card is a surface with variant, border, shadow, and radius.
  • openAlert, dismissAlert(key), dismissAlerts, showConfirmModal, showSimpleActionSheet, and hideContextMenu open and close Discord’s overlays.
  • useNavigation() is a hook that returns the current navigator, with push, pop, navigate, goBack, and setOptions.
  • setColorOpacity, darkenColor, brightenColor, and getContrastingColor take and return color strings.
  • TransitionStates, ThemeContextFlags, and WCAGContrastRatios expose Discord’s matching constants.
The onChange of TextInput and of the TextField family receives the new string, not a React Native change event.
DesignModule also has an index signature, so members that aren’t listed above are still reachable, typed as any. Discord’s ContextMenu is one of them.

Styles

Discord.createStyles builds a stylesheet that follows the current theme. Pass it a style object (or a function returning one) and it returns a hook. Call that hook inside your component to get the resolved styles.
The function returned by createStyles is a hook. It subscribes to the theme, so call it only from a component body.

Switch

Switch is Discord’s standalone toggle. Use it when you need a switch outside a TableSwitchRow.
boolean
required
Whether the switch is on.
(value: boolean) => void
required
Called with the new value when the user flips the switch.
boolean
Prevents interaction.
string
Label read by screen readers.

Other handles

These handles are untyped modules. The table shows the member Unbound uses from each one in its own UI: Portal, Forms, and HelpMessage aren’t used by Unbound itself. The table in Available handles lists the props or name each one is found by.
If the component you need isn’t here, find it yourself with Metro, for example metro.findByName('ComponentName'). To change a component Discord renders, rather than render it yourself, see Patching components.