Skip to main content

MenuRow

The browse-screen row: leading thumbnail, title, one-line subtitle, and a trailing chevron, drawn on the dropdown-option face. Every list in the shared UI — addons, guide index, config scopes, entity rosters — is built from it, so lists read as one system.

MenuRow

Import​

import { MenuRow } from '@bedrock-core/ore-styled';

Usage​

function Row(): JSX.Element {
const { navigate } = useNavigation();

return (
<MenuRow
icon={'textures/items/diamond'}
title={'Diamond'}
subtitle={'A rare gem'}
onPress={() => navigate('shop:details', { params: { id: 'diamond' } })}
/>
);
}

Props​

PropTypeDefaultDescription
title*DisplayText—First line — the row's name
subtitleDisplayText—Second line, rendered muted. Omit for a single-line row
iconstring—Leading thumbnail texture path. Omit for a text-only row
iconSizenumberthe theme's row icon sizeThumbnail edge in px
chevronbooleantrueTrailing > affordance. Set false for rows that select rather than navigate
selectedbooleanfalseWhether this row is the list's current selection. For a selecting list, where one row stands after the press
depthnumber0Indent level for nested index rows. Each step insets the row's whole box, not its contents, so a child row is visibly narrower than its section header
onPress(event: PressEvent) => unknown | Promise<unknown>—Press handler. Use to instead when the press opens another screen
toScreenKey—The screen this row opens, <addon>:<name>. A row with one is a <Link>
replacebooleanfalseWith to: take the place of the screen this row is on rather than stacking over it
titleMaxLengthnumber—Characters the title reserves, for a title only known when the screen is shown
subtitleMaxLengthnumber—Characters the subtitle reserves. Setting it also keeps the subtitle line when the subtitle is empty, so the row has one shape
enabledbooleantrueA disabled row keeps its face and greys its text

Inherits control props. It sets alignSelf: 'stretch' rather than an explicit width, so do not hard-code a width alongside depth.

An index other addons can show​

A row with to is a link, so where it leads is data rather than a handler — which is what lets an index of rows be shown by an addon running none of this one's script. A row with onPress cannot be described that way, and does nothing in a foreign realm.

<MenuRow icon={'textures/items/diamond'} title={'Diamond'} subtitle={'A rare gem'} to={'shop:diamond'} />

Reserving room for live text​

A compiled screen bakes a row's text unless told how long a live one may be. Give titleMaxLength — and subtitleMaxLength where there is a second line — for a row whose text comes from data:

<MenuRow title={addon.name} titleMaxLength={24} subtitle={addon.version} subtitleMaxLength={12} to={key} />

Localized labels​

title and subtitle are DisplayText, so a row may carry a literal, a .lang key or a RawMessage:

const { key, raw } = useTranslation(i18n);

<MenuRow
title={key($ => $.shop.title)}
subtitle={raw($ => $.shop.stock, { count })}
onPress={() => navigate('shop:home')}
/>
Color prefixes only apply to literals

MenuRow colors its lines with a § prefix, and applies it only to literal strings — a RawMessage, or a string the active resolver recognizes as a key, passes through untouched in the label's own color.

If you need a specific color on localized text, bake the § code into the authored translation value instead of the call site.

Examples​

Text-only list​

<Panel flexDirection={'column'} gap={2}>
<MenuRow title={'General'} onPress={() => open('general')} />
<MenuRow title={'Economy'} onPress={() => open('economy')} />
<MenuRow title={'Permissions'} onPress={() => open('permissions')} />
</Panel>

Nested index​

<Panel flexDirection={'column'} gap={2}>
<MenuRow title={'Getting started'} chevron={false} onPress={() => {}} />
<MenuRow depth={1} title={'Installation'} onPress={() => open('installation')} />
<MenuRow depth={1} title={'First screen'} onPress={() => open('first-screen')} />
<MenuRow title={'Reference'} chevron={false} onPress={() => {}} />
<MenuRow depth={1} title={'Components'} onPress={() => open('components')} />
</Panel>

Selection rows​

Drop the chevron for rows that pick a value rather than navigating deeper.

{themes.map(name => (
<MenuRow
title={name}
chevron={false}
enabled={name !== current}
onPress={() => setTheme(name)}
/>
))}

With an icon and a disabled state​

<MenuRow
icon={'textures/ui/config/config'}
title={'Server settings'}
subtitle={'Operators only'}
enabled={isOperator}
onPress={() => navigate('shop:server_config')}
/>

Theme tokens​

Read from theme.components.menuRow:

TokenDefault
padding4
gap4
iconSize16
textStyle.font'mojangles'
textStyle.scale1
textStyle.color'§f'
textStyle.disabledColor'§8'
textStyle.muted'§7'
textStyle.mutedDisabled'§8'
textures.backgroundthe dropdown option face
textures.backgroundSelectedthe dropdown's selected option face

Notes​

  • Use MenuRow for every list in a screen rather than hand-rolling rows.
  • Keep subtitles to one short line; both lines are clipped with an ellipsis at one line each.
  • Use depth for hierarchy instead of nesting Panels with padding — the inset box is what communicates the level.
  • Set chevron={false} whenever pressing the row does not open another screen.
  • Pair with Header above and a Scroll around the rows for a standard browse screen.