Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/felipe-software/react-native-jelly-tabs/llms.txt

Use this file to discover all available pages before exploring further.

JellyTabBar is a drop-in replacement for the default Expo Router tab bar. Pass it to the tabBar prop on the <Tabs> component and it immediately picks up your existing options — icons, badges, labels, colors, and more — with no extra configuration needed.

Basic Setup

import { Tabs } from "expo-router";
import { JellyTabBar } from "react-native-jelly-tabs";

export default function TabLayout() {
  return (
    <Tabs tabBar={(props) => <JellyTabBar {...props} />}>
      {/* your Tabs.Screen definitions */}
    </Tabs>
  );
}
The navigator supplies state, descriptors, navigation, and insets through the callback — do not pass those props manually.

Floating Mode

Pass the floating prop to absolutely position the tab bar over the screen instead of reserving layout space at the bottom. The screen content then renders behind the bar.
<Tabs tabBar={(props) => <JellyTabBar {...props} floating />}>
When floating is active, the bar gets position: "absolute" with bottom: 0, left: 0, right: 0, and zIndex: 1. The screen fills the full viewport height beneath it.
Scrollable screens need bottom padding. Because the floating bar overlays the screen, the last item in a FlatList, ScrollView, or similar component can end up hidden beneath the bar. Add a contentContainerStyle with enough paddingBottom to keep your content reachable. A value equal to the bar’s height plus the device’s safe-area inset is a reliable starting point (typically 64 + insets.bottom + 12).

Hidden Tabs with href: null

Expo Router’s href: null convention hides a route from the tab bar while still allowing it to be navigated to programmatically or via deep links. JellyTabBar fully supports this: any screen whose options include href: null is filtered out of the visible tab list, and the pill selection correctly tracks the currently focused route even when the focused route itself is hidden.
<Tabs.Screen
  name="modal"
  options={{
    href: null, // hidden from the tab bar
    title: "Modal",
  }}
/>

Supported Navigation Options

JellyTabBar reads these standard Expo Router / React Navigation options from each screen’s options object:

Icons

<Tabs.Screen
  name="home"
  options={{
    tabBarIcon: ({ color, size }) => (
      <MaterialIcons color={color} name="home" size={size} />
    ),
  }}
/>
tabBarIcon receives { color, focused, size }. JellyTabBar calls it twice — once with focused: true to produce the activeIcon rendered through the animated pill mask, and once with focused: false to produce the inactiveIcon shown in the track.

Labels

OptionEffect
titleSets the tab label when no tabBarLabel is present.
tabBarLabelString label override. Function-valued labels are not currently rendered.
tabBarLabelStyleStyleProp<TextStyle> applied to the label.
tabBarShowLabelSet to false to hide the label entirely.

Badges

options={{
  tabBarBadge: 3,
  tabBarBadgeStyle: { backgroundColor: "#ff3b30" },
}}
tabBarBadge accepts a number or string. Style it with tabBarBadgeStyle (StyleProp<TextStyle>).

Colors

These options are read from the focused screen’s descriptor and apply to the whole bar:
OptionMaps to
tabBarActiveTintColorActive icon and label color (colors.activeContent)
tabBarInactiveTintColorInactive icon and label color (colors.inactiveContent)
tabBarActiveBackgroundColorSelected pill color (colors.selectedSurface)
tabBarInactiveBackgroundColorTrack background color (colors.surface)
Pass the colors prop directly to JellyTabBar to override any of these with a static value that takes priority over the navigation option.

Backdrop

import { BlurView } from "expo-blur";

<Tabs tabBar={(props) => (
  <JellyTabBar
    {...props}
    backdrop={<BlurView intensity={60} style={StyleSheet.absoluteFill} />}
  />
)} />
tabBarBackground is also supported as a standard option — the function is called and its output is passed as the bar’s backdrop node, rendered beneath the track’s color layer. Providing a backdrop prop directly takes precedence.

Style Overrides

Prop / OptionTypeDescription
tabBarStyleStyleProp<ViewStyle> (option)Extra styles applied to the bar’s outer container.
containerStyleStyleProp<ViewStyle> (prop)Wrapper override with highest priority, applied after tabBarStyle.
maxWidthDimensionValueMaximum track width (default 400). The bar centers itself within wider parents.

Accessibility & Testing

OptionEffect
tabBarAccessibilityLabelSets accessibilityLabel on the tab’s accessibility view. Falls back to the tab label.
tabBarButtonTestIDSets testID on the tab’s accessibility view.

Full Working Example

A three-tab Expo Router layout with floating mode, icons, and a hidden tab:
// app/_layout.tsx
import "react-native-gesture-handler";

import { MaterialIcons } from "@react-native-vector-icons/material-icons";
import { Tabs } from "expo-router";
import { JellyTabBar } from "react-native-jelly-tabs";
import { StatusBar } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";

export default function RootLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <SafeAreaProvider>
        <StatusBar barStyle="dark-content" backgroundColor="#f5f1e8" />
        <Tabs
          screenOptions={{ headerShown: false }}
          tabBar={(props) => <JellyTabBar {...props} floating />}
        >
          <Tabs.Screen
            name="index"
            options={{
              title: "Home",
              tabBarIcon: ({ color, size }) => (
                <MaterialIcons color={color} name="home" size={size} />
              ),
            }}
          />
          <Tabs.Screen
            name="search"
            options={{
              title: "Search",
              tabBarIcon: ({ color, size }) => (
                <MaterialIcons color={color} name="search" size={size} />
              ),
            }}
          />
          <Tabs.Screen
            name="profile"
            options={{
              title: "Profile",
              tabBarIcon: ({ color, size }) => (
                <MaterialIcons color={color} name="person" size={size} />
              ),
            }}
          />
          {/* This screen is navigable but hidden from the tab bar */}
          <Tabs.Screen
            name="settings"
            options={{
              href: null,
              title: "Settings",
            }}
          />
        </Tabs>
      </SafeAreaProvider>
    </GestureHandlerRootView>
  );
}
Wrap your app root with GestureHandlerRootView (required by React Native Gesture Handler v2+) and SafeAreaProvider so that the tab bar correctly accounts for device safe-area insets.

Build docs developers (and LLMs) love