Skip to content

feat(iOS, Stack): add support for toolbar items#4013

Open
johankasperi wants to merge 14 commits into
software-mansion:mainfrom
johankasperi:johankasperi/ios-stack-toolbar
Open

feat(iOS, Stack): add support for toolbar items#4013
johankasperi wants to merge 14 commits into
software-mansion:mainfrom
johankasperi:johankasperi/ios-stack-toolbar

Conversation

@johankasperi
Copy link
Copy Markdown
Contributor

@johankasperi johankasperi commented May 11, 2026

Description

Inspired by the toolbar functionality in expo router this PR introduces a very similar API as headerBarButtonItems to the stack navigator but instead of creating UIBarButtonItem for the header, it creates UIBarButtonItem for the screen toolbar (bottom of the screen).

Changes

Adds a new prop to ScreenStackHeaderConfigProps named toolbarItems?: ToolbarItem[] | undefined;. Uses this config to generate an array of UIBarButtonItem and sets this array onto the current view controller with setToolbarItems.

Before & after - visual documentation

Before

N/A

After

Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-05-11.at.15.21.17.mov

Test plan

Run the example app and open the new example named "Toolbar"

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

johankasperi and others added 9 commits May 11, 2026 14:31
Exposes UINavigationController's bottom toolbar to stack screens via a
new `toolbarItems` prop on `ScreenStackHeaderConfig`. Toolbar items use
the existing `RNSBarButtonItem` infrastructure and support buttons,
menus, flexible space, and fixed spacing, matching the header bar button
API.

The react-navigation `native-stack` adapter in the submodule bridges
this as `unstable_toolbarItems` on `NativeStackNavigationOptions`, with
a `processToolbarItems` helper that mirrors `processBarButtonItems`.

Key changes:
- `src/types.tsx`: add `ToolbarItemFlexibleSpace` and `ToolbarItem` types;
  add `toolbarItems` prop to `ScreenStackHeaderConfigProps`
- `src/fabric/ScreenStackHeaderConfigNativeComponent.ts`: add
  `toolbarItems`, `onPressToolbarItem`, `onPressToolbarMenuItem` props
- `src/components/helpers/prepareHeaderBarButtonItems.ts`: extend to
  accept `'toolbar'` side and pass through `flexibleSpace` items
- `src/components/ScreenStackHeaderConfig.tsx`: prepare and forward
  toolbar items; wire press handlers
- `ios/RNSScreenStackHeaderConfig.h`: declare `toolbarItems` property
- `ios/RNSScreenStackHeaderConfig.mm`: add `toolbarItemsFromConfigs:`
  instance method; map `toolbarItems` prop in `updateProps:oldProps:`;
  call `setToolbarItems:` / `setToolbarHidden:` in
  `updateViewController:withConfig:animated:`
- `apps/`: add Toolbar playground with 7 demo screens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 'AdvancedMenus' demo screen to the Toolbar playground showing
multi-level nested submenus, inline sections, and destructive actions
across Format and View menu buttons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Demonstrates labelStyle on toolbar button items: bold/italic font weight,
small/large font size, and custom label colors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fontStyle was never read from the titleStyle config dict, and
RCTFont was called with style:nil unconditionally. Read fontStyle
from the dict and pass it through, and expand the condition to
enter the RCTFont path whenever fontStyle is set (even without
fontFamily/fontWeight).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduce to three items and add a custom font family with italic style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hide toolbar (setToolbarItems:nil + setToolbarHidden:YES) in the
  shouldHide early-return branch so toolbar does not persist when
  navigating to a screen with headerShown: false
- Extract duplicate findInMenu to module scope in ScreenStackHeaderConfig.tsx
- Add JSDoc to ToolbarItemFlexibleSpace and ToolbarItem in types.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@johankasperi johankasperi changed the title feat(iOS, Stack): add support for toolbar feat(iOS, Stack): add support for toolbar items May 11, 2026
johankasperi and others added 5 commits May 12, 2026 23:45
UIBarButtonItemBadge is not rendered by UIKit when the item is in a
UIToolbar. Exclude badge from ToolbarItem in types.tsx (explicit union
over Omit<…, 'badge'> so the discriminated union is preserved) and
update prepareHeaderBarButtonItems to use the badge-capable base type
so header bar button items (left/right) retain badge support.
Remove BadgeItemsScreen example and debug NSLog.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kkafar kkafar self-requested a review May 14, 2026 12:53
@kkafar
Copy link
Copy Markdown
Member

kkafar commented May 14, 2026

Hey @johankasperi,

thank you for the contribution. We'll find time to review this, but FYI it's not a priority atm.

We are currently focused on delivering next-major-related scope and we assign low priority to any new features targeted at currently stable major version.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds iOS toolbar item support for stack headers, exposing a new toolbar item configuration path from TypeScript through Fabric/native iOS and adding documentation plus an example playground.

Changes:

  • Adds toolbarItems public types and native component props/events.
  • Builds and applies UIBarButtonItem toolbar items on iOS, including flexible/fixed spacing and menu/action callbacks.
  • Adds guide documentation and a new Toolbar example screen in the example app.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/types.tsx Adds toolbarItems and ToolbarItem typings.
src/fabric/ScreenStackHeaderConfigNativeComponent.ts Adds native toolbar item prop and press events.
src/components/ScreenStackHeaderConfig.tsx Prepares toolbar items and dispatches toolbar callbacks.
src/components/helpers/prepareHeaderBarButtonItems.ts Extends item preparation for toolbar/flexible space support.
ios/RNSScreenStackHeaderConfig.mm Applies toolbar items to the view controller and emits toolbar events.
ios/RNSScreenStackHeaderConfig.h Declares stored toolbar item configs.
ios/RNSBarButtonItem.mm Adds fontStyle support for bar button title styling.
guides/GUIDE_FOR_LIBRARY_AUTHORS.md Documents the new toolbar items API.
apps/src/screens/Toolbar.tsx Adds a playground for toolbar item scenarios.
apps/Example.tsx Registers the Toolbar playground screen.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ios/RNSBarButtonItem.mm
NSString *fontFamily = titleStyle[@"fontFamily"];
NSNumber *fontSize = titleStyle[@"fontSize"];
NSString *fontWeight = titleStyle[@"fontWeight"];
NSString *fontStyle = titleStyle[@"fontStyle"];
```typescript
<Stack.Screen
options={{
toolbarItems: [
useEffect(() => {
navigation.setOptions({
title: `Dynamic Items (${count})`,
unstable_toolbarItems: () => [
@johankasperi
Copy link
Copy Markdown
Contributor Author

Hey @johankasperi,

thank you for the contribution. We'll find time to review this, but FYI it's not a priority atm.

We are currently focused on delivering next-major-related scope and we assign low priority to any new features targeted at currently stable major version.

Thank you! Totally understand that it isn't a priority. There is no hurry from my side either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants