IconButton
Icon buttons help people take supplementary actions with a single tap. Follows the Material Design 3 Icon Button specification.
The icon and selectedIcon props accept three forms — a string name (resolved via the theme's iconResolver, for example mdiResolver for MaterialDesignIcons), a pre-rendered React element from any icon library, or a render function that receives the resolved { size, color }. See the Icons guide for full details, including how to register a global iconResolver for Lucide, SF Symbols, or custom SVGs.
Usage
import { IconButton } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
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, alignItems: 'center', justifyContent: 'center' }}>
<IconButton
icon="heart-outline"
accessibilityLabel="Favorite"
onPress={() => {}}
/>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Variants
| Variant | Use case |
|---|---|
filled | High emphasis, filled container |
tonal | Medium emphasis, tonal container |
outlined | Medium emphasis, outlined border |
standard | Low emphasis, no container |
Sizes
Five MD3 Expressive sizes. small is the default.
size | Height | Icon |
|---|---|---|
extraSmall | 32dp | 20dp |
small | 40dp | 24dp |
medium | 56dp | 24dp |
large | 96dp | 32dp |
extraLarge | 136dp | 40dp |
import { IconButton } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
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: 12 }}>
<IconButton icon="star" size="extraSmall" accessibilityLabel="Star" onPress={() => {}} />
<IconButton icon="star" size="small" accessibilityLabel="Star" onPress={() => {}} />
<IconButton icon="star" size="medium" accessibilityLabel="Star" onPress={() => {}} />
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Width and shape
width trades horizontal padding at a fixed height — 'narrow', 'uniform'
(default), or 'wide'. At size="small" that is 32 / 40 / 52dp wide.
shape controls the resting container: 'round' (default) rests as a
circle/pill, 'square' rests at a size-dependent corner. Toggle buttons invert
their shape when selected, so a round toggle squares off to signal the change.
<IconButton icon="star" width="wide" accessibilityLabel="Star" />
<IconButton icon="star" shape="square" accessibilityLabel="Star" />
Bring your own icon library
import { IconButton } from '@rootnative/components'
import { Heart } from 'lucide-react-native'
<IconButton
icon={({ size, color }) => <Heart size={size} color={color} />}
accessibilityLabel="Like"
/>
The render-function form receives the size and color the IconButton would have given to its default icon (24dp at the default small size, plus the variant + state color), so the icon stays in sync with size/variant changes automatically.
Toggle Mode
Use selected and selectedIcon for toggle behavior. selected with
onSelectedChange is the controlled form. defaultSelected is the
uncontrolled form: the button flips its own state on press and reports the
new state through onSelectedChange.
import { IconButton } from '@rootnative/components'
import { mdiResolver } from '@rootnative/components/mdi'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { useState } from 'react'
import { View } from 'react-native'
export default function App() {
const [liked, setLiked] = useState(false)
return (
<SafeAreaProvider>
<ThemeProvider iconResolver={mdiResolver}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<IconButton
icon="heart-outline"
selectedIcon="heart"
selected={liked}
onSelectedChange={setLiked}
accessibilityLabel={liked ? 'Unlike' : 'Like'}
/>
</View>
</ThemeProvider>
</SafeAreaProvider>
)
}
Props
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
icon | IconSource | - | Yes | Icon to display. Accepts a string name (resolved via the theme's `iconResolver`, for example `mdiResolver`), a pre-rendered element, or a render function that receives `{ size, color }`. |
selectedIcon | IconSource | - | No | Icon to display when `selected` is `true` (toggle mode). |
iconColor | string | - | No | Overrides the automatic icon color derived from the variant and state. In the `leading` or `trailing` slot of an `AppBar`, the automatic color of a `'standard'` button is the icon color of that slot, and its hover and press layers derive from that color. A selected toggle keeps `primary`. |
contentColor | string | - | No | Override the content (icon) color. Takes precedence over `iconColor` when both are provided. |
containerColor | string | - | No | Override the container (background) color. State-layer colors (hover, press) are derived automatically. |
style | StyleProp<ViewStyle> | - | No | Style for the component. The keys that place or size it in its parent — `alignSelf`, `flex`, `flexGrow`, `flexShrink`, `flexBasis`, `width`, the margins, `position`, the insets and `zIndex` — go to the outer wrapper, so the focus ring and the shadow move with it. `minWidth` and `maxWidth` go with a `width`; alone, they style the container. The wrapper sets no `alignSelf`, so the parent's `alignItems` places the component. In a parent that stretches its children, the component keeps its own width at the start edge. Every other key styles the container. Static form only — the function form `(state) => style` is not supported because the component drives its container background through Reanimated. Use `containerColor` / `contentColor` for state-aware styling. |
onPress | () => void | - | No | Called when the button is pressed. |
disabled | boolean | | No | Disables the button. |
variant | enum | filled | No | Visual style variant. |
selected | boolean | - | No | Enables toggle mode and controls it. The button changes appearance based on the selected/unselected state. Pair with `onSelectedChange`, or use `defaultSelected` for an uncontrolled toggle. |
defaultSelected | boolean | - | No | Enables toggle mode without controlling it: the button flips its own state on press and reports it through `onSelectedChange`. |
onSelectedChange | (selected: boolean) => void | - | No | Called with the next state when a toggle button is pressed. |
size | enum | small | No | MD3 Expressive size — one of `'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge'`. Sets container height and icon size. |
width | enum | uniform | No | Width variant. `'uniform'` is a square container; `'narrow'`/`'wide'` adjust horizontal padding around the icon. |
shape | enum | round | No | Container shape. `'round'` is a pill/circle; `'square'` rests at a size-dependent corner. Toggle buttons invert the shape when selected. |
accessibilityLabel | string | - | Yes | Required — icon-only buttons must have a label for screen readers. |
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: PressableKeyDownEvent) => void | - | No | - |