One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Toolbar and Menus

Declarative Stack.Toolbar items plus the ToolbarHost, BarButtonItem, and MenuAction primitives on iOS

Declarative native toolbar items through Stack.Toolbar, in the same compound style as Stack.Header. One toolbar declares one placement: left and right compile to native header items while bottom, the default, renders a native UIToolbar at the bottom of the screen. This is the UIKit toolbar, distinct from the SwiftUI toolbar in iOS Navigation and Toolbar.

For explicit item identifiers or native item props the declarative API does not forward, drop to the lower-level primitives: One.iOS.ToolbarHost with One.iOS.BarButtonItem children, each a UIBarButtonItem, and One.iOS.MenuAction for UIMenu and UIAction elements.

Usage

import { Stack } from 'one'
function IndexToolbar({ edit, share }: { edit: () => void; share: () => void }) {
return (
<Stack.Screen name="index">
<Stack.Toolbar placement="left">
<Stack.Toolbar.Button icon="pencil" onPress={edit}>
Edit
</Stack.Toolbar.Button>
</Stack.Toolbar>
<Stack.Toolbar placement="right">
<Stack.Toolbar.Menu title="More" icon="ellipsis.circle">
<Stack.Toolbar.MenuAction icon="square.and.arrow.up" onPress={share}>
Share
</Stack.Toolbar.MenuAction>
</Stack.Toolbar.Menu>
</Stack.Toolbar>
</Stack.Screen>
)
}

Each toolbar maps to header items on its side. Buttons take their label as text children, an icon SF Symbol name, and onPress; menus nest MenuAction children and Menu submenus.

How it works

Stack.Toolbar children are descriptors, never rendered views: the converters read them into native items. left maps to unstable_headerLeftItems and right to unstable_headerRightItems, the iOS header-item path in react-navigation, and forces the header visible. bottom renders through a toolbar host inside the screen’s view controller, with each button or menu becoming a UIBarButtonItem and menu actions becoming UIMenu and UIAction elements.

Options are route-local and dynamic. A toolbar rendered in route content applies through the screen’s navigation options, so screen state can drive items with no new API; asChild renders children as a custom header element instead of header items. An explicit toolbar always wins over incoming options on its own side: hidden items convert to nothing, so a toolbar whose items are all hidden writes an empty array and clears inherited items, while an absent toolbar preserves them.

Header items

left and right toolbars live in layout config or route content and set header items through navigation options. Children are Button, Menu, and Spacer descriptors; anything else warns in dev and is skipped. Spacer needs a fixed width here; the flexible form is bottom only.

import { Stack } from 'one'
function EditToolbar({ edit }: { edit: () => void }) {
return (
<Stack.Toolbar placement="left">
<Stack.Toolbar.Button icon="pencil" onPress={edit}>
Edit
</Stack.Toolbar.Button>
<Stack.Toolbar.Spacer width={12} />
</Stack.Toolbar>
)
}

Buttons take text children or a Label child, icon as an SF Symbol name or image source, tintColor including dynamic iOS values, variant, disabled, hidden, selected, and onPress. separateBackground opts out of the shared background and hidesSharedBackground hides it behind the item for iOS 26 Liquid Glass. Badge is left and right only; bottom throws in dev.

Bottom toolbar

bottom renders the navigation-controller toolbar in place and must mount in screen content, where the responder chain reaches the screen view controller. In layout config it warns and is ignored. Bottom items take the same props; the bottom bar takes SF Symbol strings for icons. Hide the bar by unmounting the toolbar or by setting hidden on its items.

import { Stack } from 'one'
function ComposerToolbar({ add, hidden }: { add: () => void; hidden: boolean }) {
return (
<Stack.Toolbar>
<Stack.Toolbar.Button icon="plus" hidden={hidden} onPress={add}>
Add
</Stack.Toolbar.Button>
<Stack.Toolbar.Spacer />
<Stack.Toolbar.SearchBarSlot />
</Stack.Toolbar>
)
}

Spacer without a width is flexible here, and SearchBarSlot embeds the screen search bar. The bottom toolbar renders nothing off iOS.

ToolbarHost

One.iOS.ToolbarHost is the lower-level bottom-UIToolbar renderer that a bottom Stack.Toolbar builds on. Reach for it directly only when you need explicit item identifiers or native item props the declarative API does not forward.

import { One } from 'one'
function BottomBar({ edit }: { edit: () => void }) {
return (
<One.iOS.ToolbarHost>
<One.iOS.BarButtonItem identifier="edit" title="Edit" systemImageName="pencil" onSelected={edit} />
</One.iOS.ToolbarHost>
)
}
ToolbarHostTypeDescription
childrenReact.ReactNodeBarButtonItem children.
React.ReactNodeBarButtonItem children.
hiddenbooleanHides the navigation controller toolbar.
booleanHides the navigation controller toolbar.
animatedbooleanAnimate show and hide. Defaults to true.
booleanAnimate show and hide. Defaults to true.
BarButtonItemTypeDescription
childrenReact.ReactNodeLabel content.
React.ReactNodeLabel content.
identifier (required)stringUnique ID.
stringUnique ID.
titlestringButton title.
stringButton title.
systemImageNamestringSF Symbol name.
stringSF Symbol name.
xcassetNamestringXcode asset catalog image name.
stringXcode asset catalog image name.
imageImageSourcePropTypeCustom image source.
ImageSourcePropTypeCustom image source.
imageRenderingMode'template' | 'original'How the image draws.
'template' | 'original'How the image draws.
type'normal' | 'fixedSpacer' | 'fluidSpacer' | 'searchBar'Item kind.
'normal' | 'fixedSpacer' | 'fluidSpacer' | 'searchBar'Item kind.
tintColorColorValueItem tint, including dynamic iOS values.
ColorValueItem tint, including dynamic iOS values.
hidesSharedBackgroundbooleanHides the shared background behind the item.
booleanHides the shared background behind the item.
sharesBackgroundbooleanJoins the shared glass background.
booleanJoins the shared glass background.
barButtonItemStyle'plain' | 'done' | 'prominent'Button style.
'plain' | 'done' | 'prominent'Button style.
widthnumberFixed width for spacers.
numberFixed width for spacers.
hiddenbooleanHides the item.
booleanHides the item.
selectedbooleanSelected state.
booleanSelected state.
badgeConfiguration{ value?: string backgroundColor?: ColorValue } & BasicTextStyleBadge value, color, and text style. iOS 26 or later.
{ value?: string backgroundColor?: ColorValue } & BasicTextStyleBadge value, color, and text style. iOS 26 or later.
titleStyleBasicTextStyleFont family, size, weight, and color of the title.
BasicTextStyleFont family, size, weight, and color of the title.
accessibilityLabelstringAccessibility label.
stringAccessibility label.
accessibilityHintstringAccessibility hint.
stringAccessibility hint.
disabledbooleanDisables the item.
booleanDisables the item.
onSelected() => voidTap handler.
() => voidTap handler.

One.iOS.MenuAction is a native iOS UIMenu and UIAction element for context menus in toolbars. icon takes an SF Symbol name.

import { One } from 'one'
function RowActions() {
return (
<>
<One.iOS.MenuAction
identifier="edit"
title="Edit"
icon="pencil"
onSelected={() => console.log('edit')}
/>
<One.iOS.MenuAction
identifier="delete"
title="Delete"
icon="trash"
destructive
onSelected={() => console.log('delete')}
/>
</>
)
}
MenuActionTypeDescription
identifier (required)stringUnique ID.
stringUnique ID.
title (required)stringAction title.
stringAction title.
labelstringLabel override.
stringLabel override.
iconstringSF Symbol name.
stringSF Symbol name.
xcassetNamestringXcode asset catalog image name.
stringXcode asset catalog image name.
imageImageSourcePropTypeCustom image source.
ImageSourcePropTypeCustom image source.
imageRenderingMode'template' | 'original'How the image draws.
'template' | 'original'How the image draws.
childrenReact.ReactNodeNested menu content.
React.ReactNodeNested menu content.
disabledbooleanDisables the action.
booleanDisables the action.
destructivebooleanDestructive styling.
booleanDestructive styling.
discoverabilityLabelstringDiscoverability hint.
stringDiscoverability hint.
subtitlestringSubtitle under the title.
stringSubtitle under the title.
accessibilityLabelstringAccessibility label.
stringAccessibility label.
accessibilityHintstringAccessibility hint.
stringAccessibility hint.
displayAsPalettebooleanShows children as a palette.
booleanShows children as a palette.
displayInlinebooleanShows children inline.
booleanShows children inline.
preferredElementSize'auto' | 'small' | 'medium' | 'large'Preferred size of menu elements.
'auto' | 'small' | 'medium' | 'large'Preferred size of menu elements.
isOnbooleanOn state for toggle actions.
booleanOn state for toggle actions.
keepPresentedbooleanKeeps the menu open after the tap.
booleanKeeps the menu open after the tap.
hiddenbooleanHides the action.
booleanHides the action.
tintColorColorValueAction tint.
ColorValueAction tint.
barButtonItemStyle'plain' | 'done' | 'prominent'Button style.
'plain' | 'done' | 'prominent'Button style.
sharesBackgroundbooleanJoins the shared glass background.
booleanJoins the shared glass background.
hidesSharedBackgroundbooleanHides the shared background behind the item.
booleanHides the shared background behind the item.
onSelected() => voidTap handler.
() => voidTap handler.
singleSelectionbooleanSingle selection among siblings.
booleanSingle selection among siblings.
titleStyleBasicTextStyleFont family, size, weight, and color of the title.
BasicTextStyleFont family, size, weight, and color of the title.

Platform differences

iOSAndroidWeb
Header itemsNative itemsNoneNone
Bottom barUIToolbarRenders nullRenders null
PrimitivesNative viewsRenders nullRenders null

Limits and gotchas

Credit

The native toolbar, menu action, and font helpers are adapted from Expo Router.

Edit this page on GitHub.