Skip to main content

Badge

Badges show a small dot or a short count on an icon. A badge with no label is a 6dp dot. A badge with a label is a 16dp pill that holds a count or a short word.

Usage​

Wrap the anchor, usually a 24dp icon, and the badge sits at the anchor's top-end corner with the MD3 offsets.

import { Badge } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { MaterialDesignIcons } from '@react-native-vector-icons/material-design-icons'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 32 }}>
<Badge accessibilityLabel="New activity">
<MaterialDesignIcons name="bell-outline" size={24} color="#49454F" />
</Badge>
<Badge label={3}>
<MaterialDesignIcons name="email-outline" size={24} color="#49454F" />
</Badge>
<Badge label={1200}>
<MaterialDesignIcons name="message-outline" size={24} color="#49454F" />
</Badge>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Sizes​

The size follows from label. There is no size prop.

LabelSizeAnatomy
nonesmall6dp dot, no content
number or stringlarge16dp tall pill, labelSmall text, 4dp side padding, min width 16dp

Counts and max​

A number above max renders as {max}+. The default max is 999, so 1200 renders as 999+. Set max to a lower number for a narrower badge.

import { Badge } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { MaterialDesignIcons } from '@react-native-vector-icons/material-design-icons'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { View } from 'react-native'

export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 32 }}>
<Badge label={120} max={99}>
<MaterialDesignIcons name="message-outline" size={24} color="#49454F" />
</Badge>
<Badge label="NEW">
<MaterialDesignIcons name="star-outline" size={24} color="#49454F" />
</Badge>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}

Visibility​

Set visible={false} to hide the badge. The anchor keeps its layout, so the badge can appear later without a shift.

<Badge label={count} visible={count > 0}>
<MaterialDesignIcons name="cart-outline" size={24} color="#49454F" />
</Badge>

Standalone​

Without children the badge renders inline on its own, for example next to a list headline.

<Badge label={12} />

In a NavigationBar​

Each NavigationBar item accepts a badge value. true renders a dot. A number or a string renders a large badge.

<NavigationBar
items={[
{ value: 'home', label: 'Home', icon: 'home-outline' },
{
value: 'inbox',
label: 'Inbox',
icon: 'email-outline',
badge: 7,
accessibilityLabel: 'Inbox, 7 unread',
},
{
value: 'alerts',
label: 'Alerts',
icon: 'bell-outline',
badge: true,
accessibilityLabel: 'Alerts, new activity',
},
]}
/>

Accessibility​

A large badge is text and a screen reader reads it. A small badge has no text, so give it an accessibilityLabel, for example "New activity".

Inside a NavigationBar the icon block is hidden from screen readers, and the badge with it. Put the count in the item's accessibilityLabel.

Custom colors​

Override the fill with containerColor and the text with contentColor. The defaults are error and onError.

<Badge label={5} containerColor="#6750A4" contentColor="#FFFFFF">
<MaterialDesignIcons name="bell-outline" size={24} color="#49454F" />
</Badge>

Props​

PropTypeDefaultRequiredDescription
labelstring | number-NoContent of the badge. A number or a short string, for example a count. Omit it for a small dot. A number above `max` renders as `{max}+`.
maxnumber999NoThe largest number the badge shows before it truncates to `{max}+`. Applies to numeric labels only.
visiblebooleanNoWhether the badge renders. When `false`, the anchor still renders and keeps its layout, so a badge can appear later without a shift.
childrenReactNode-NoThe element the badge anchors to, usually a 24dp icon. The badge sits at the anchor's top-end corner per the MD3 offsets. Without children the badge renders inline on its own.
containerColorstringerrorNoOverride the badge fill.
contentColorstringonErrorNoOverride the label color.
styleStyleProp<ViewStyle>-NoStyle applied to the badge itself, not to the anchor wrapper.
wrapperStyleStyleProp<ViewStyle>-NoStyle applied to the wrapper that holds the anchor and the badge.
accessibilityLabelstring-NoScreen-reader text for the badge, for example "3 unread messages". A small badge has no text of its own, so without this label a screen reader does not announce it.
testIDstring-NoTest id applied to the badge. The anchor wrapper, when children are given, carries `<testID>-wrapper`.
hrefAttrs{ target?: string; rel?: string; download?: string | boolean; }-NoWeb only. The `target`, `rel` and `download` of the `<a>` that a view with an `href` renders, for example under Expo Router's `<Link asChild>`. `target: '_blank'` opens the link in a new tab. Native ignores it.
onKeyDown(event: { nativeEvent: { key?: string; }; }) => void-No-