Skip to content

Custom Bottom bar for iOS and Android#8307

Open
markdevocht wants to merge 15 commits into
masterfrom
feat/Custom-bottom-bar-buttons
Open

Custom Bottom bar for iOS and Android#8307
markdevocht wants to merge 15 commits into
masterfrom
feat/Custom-bottom-bar-buttons

Conversation

@markdevocht
Copy link
Copy Markdown
Contributor

Summary

Adds custom bottom tab items: render a registered React component per tab instead of native icon + label, with optional floating row chrome via bottomTabs.customRow. Same JavaScript API on iOS and Android.

Native BottomTabs / UITabBar still owns selection and layout state. Visible bar chrome is hidden; a floating custom row hosts one React surface per tab in equal-width slots.

Motivation

Apps need tab bars with Lottie/SVG icons, custom badges, and fully custom chrome without reimplementing tab selection, stack switching, popToRoot, hardware back history, or navigation events.

API

bottomTab.component (required on every tab)

Navigation.registerComponent('MyTabItem', () => require('./MyTabItem'));

Navigation.setRoot({
  root: {
    bottomTabs: {
      options: {
        bottomTabs: {
          customRow: {
            backgroundEffect: 'glass',
            cornerRadius: 28,
            horizontalMargin: 16,
            bottomMargin: 0,
            height: 67,
          },
        },
      },
      children: [
        {
          stack: {
            children: [{ component: { name: 'HomeScreen' } }],
            options: {
              bottomTab: {
                component: { name: 'MyTabItem' },
                badge: '3',
              },
            },
          },
        },
        // ... every tab must set bottomTab.component
      ],
    },
  },
});

Props delivered to the tab item component: componentId, tabIndex, selected, badge (updated when selection or badge changes).

passProps from bottomTab.component.passProps are merged at creation time only; use tabIndex inside the component for per-tab UI.

If only some tabs set component, RNN logs a warning and falls back to native rendering for all tabs in that bar.

Per-tab visual options ignored when component is set: text, icon, selectedIcon, SF symbols, icon/text colors and sizes, etc. Native still handles selection, visibility, drawBehind, animations, and dotIndicator.

bottomTabs.customRow (optional)

Option Description
height Row content height (pt/dp), excluding safe area
backgroundColor Solid fill; overrides backgroundEffect
backgroundEffect 'glass' | 'blur' | 'none'
cornerRadius Row corner radius
horizontalMargin Left/right inset from screen edges
bottomMargin Gap above safe-area bottom

Applies on setRoot, showModal, mergeOptions, and setDefaultOptions.

Events

Listener Custom row
registerBottomTabPressedListener Yes (iOS + Android)
registerBottomTabSelectedListener Yes
registerBottomTabLongPressedListener No — iOS-only, wired to native UITabBar long press (hidden for custom tabs)

selectTabOnPress: false, popToRoot, and Android hardware-back tab history behave the same as with native bottom tabs.

Implementation

iOS

  • RNNBottomTabsCustomRow — hosts React tab cells, applies customRow, layout and taps
  • RNNBottomTabsController — hides native bar visuals, attaches custom row, routes taps through existing selection/event path
  • iOS 26+ defaults: glass, 28pt radius, 16pt horizontal margin; tabBarMinimizeBehavior forced to never when custom items are active

Android

  • New com.reactnativenavigation.customrow package (BottomTabsCustomRow, attacher, layout, module, config store)
  • BottomTabs.setExternalCustomItemViewHost(true) — custom row owns React item views
  • AndroidCustomRowForwarder — forwards customRow from navigation commands before layout
  • Row placement anchored to native BottomTabs bottom; avoids double-counting nav insets and layout idle loops

JS

  • Options.tsbottomTab.component, bottomTabs.customRow
  • Commands.ts — Android custom-row forwarding on relevant commands

Playground

Layouts → BottomTabs Custom Component — modal with glass custom row and shared CustomBottomTabItem (RN primitive icons, badge on Search tab).

Key files:

  • playground/src/screens/LayoutsScreen.tsxbottomTabsWithCustomComponent
  • playground/src/screens/CustomBottomTabItem.tsx
  • playground/src/screens/CustomBottomTabContentScreen.tsx

Documentation

  • New guide: website/docs/docs/docs-customBottomTabs.mdx (basic example, tab item example, customRow, events, platform notes)
  • API: options-bottomTab.mdx (component), options-bottomTabs.mdx (customRow)
  • Events: registerBottomTabPressedListener documented; custom-tab event parity noted
  • Linked from bottom tabs guide + sidebar

Test plan

  • Detox playground/e2e/CustomBottomTabComponent.test.js — 7 tests (render, badge, tab switching)
  • Android Release (android.emu.release) — 7/7 pass
  • iOS Release (ios.sim.release) — 7/7 pass
  • Manual: playground Layouts → BottomTabs Custom Component on device (optional)
  • Engine smoke if merging to Wix app (custom tabs are opt-in via bottomTab.component)

Notes for reviewers

  • Min tabs: iOS 2 (UIKit), Android 3 (AHBottomNavigation) — same as native bottom tabs
  • Long press on custom items not supported; follow-up if product needs it
  • Android backgroundEffect: 'glass' uses material chrome (no system glass API); iOS 26+ uses UIGlassEffect where available

@aattalla
Copy link
Copy Markdown

Thanks a lot, Mark, for such an initiative and great work you are doing recently. Much appreciated and highly needed. ❤️🙏🏻

A few questions, confirmations, and brainstorming ideas:

  • I’d suggest supporting the (heavy press & click to refresh) features and make them configurable if they are not already supported/planned as they will be good features.
  • Sometimes, we need to pass images, lotties, or SVGs via CDN APIs to save app bundle size and avoid making releases for each icons changes. Is this cdn api driven also supported?
  • For some tabs, we sometimes need also to only display the image, lottie, or SVG alone without the text below it. Is this configurable as well? If yes, would this image take up the entire tab position, including the text area, to not look weird as something is missing below it.
  • Have we thought of also adding shimmer support on the tabs icons till loads? This will also be a good feature to have.
  • Have we thought of supporting multiple rows of bottom tabs? Something like making bottom tabs rows dynamic based on how many tabs can be configured (if they're 10 tabs, and screen size can take only 5 tabs in one row, so there will be 2 rows, each one contains 5 tabs)?
  • Do we also support a configuration option for hide/unhide a specific tab?

@markdevocht markdevocht requested a review from yedidyak May 20, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants