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.
| Label | Size | Anatomy |
|---|---|---|
| none | small | 6dp dot, no content |
| number or string | large | 16dp 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
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
label | string | number | - | No | Content 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}+`. |
max | number | 999 | No | The largest number the badge shows before it truncates to `{max}+`. Applies to numeric labels only. |
visible | boolean | | No | Whether the badge renders. When `false`, the anchor still renders and keeps its layout, so a badge can appear later without a shift. |
children | ReactNode | - | No | The 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. |
containerColor | string | error | No | Override the badge fill. |
contentColor | string | onError | No | Override the label color. |
style | StyleProp<ViewStyle> | - | No | Style applied to the badge itself, not to the anchor wrapper. |
wrapperStyle | StyleProp<ViewStyle> | - | No | Style applied to the wrapper that holds the anchor and the badge. |
accessibilityLabel | string | - | No | Screen-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. |
testID | string | - | No | Test id applied to the badge. The anchor wrapper, when children are given, carries `<testID>-wrapper`. |
hrefAttrs | { target?: string; rel?: string; download?: string | boolean; } | - | No | Web 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 | - |