Expo RouterNative TabsLiquid GlassNavigation

Expo Router Native Tabs: Liquid Glass Tab Bars (SDK 58 Guide)

Build a real native tab bar with Expo Router NativeTabs — iOS 26+ Liquid Glass, Material on Android, SF Symbols and Material icons, badges, a search tab, and minimize-on-scroll. Updated for the stable expo-router/native-tabs API in SDK 58.

DeveloperMill3 min read

Apple's Liquid Glass redesign made custom JavaScript tab bars look dated overnight. The good news is that Expo Router's NativeTabs renders the real system tab bar, so your app gets Liquid Glass on iOS 26+, Material styling on Android, and native behaviors like scroll-to-top without writing a single animation.

As of Expo SDK 58, native tabs are stable. This guide covers the current API.

JavaScript tabs vs native tabs

Expo Router gives you two ways to build tabs:

  • Tabs (from expo-router) is built on React Navigation's bottom tabs and drawn in JavaScript. It's fully customizable and looks the same everywhere.
  • NativeTabs (from expo-router/native-tabs) uses the OS tab bar. It looks native, animates natively, and gets new system features (like Liquid Glass) automatically.

Rule of thumb: start with NativeTabs. Move to a custom bar only when the design truly needs something the system can't do.

Set up native tabs

Put your tab screens in a group and add a layout:

app/
  _layout.tsx
  (tabs)/
    _layout.tsx
    index.tsx
    explore.tsx
    settings.tsx
// app/(tabs)/_layout.tsx
import { NativeTabs } from "expo-router/native-tabs"

export default function TabLayout() {
  return (
    <NativeTabs>
      <NativeTabs.Trigger name="index">
        <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
        <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
      </NativeTabs.Trigger>

      <NativeTabs.Trigger name="explore">
        <NativeTabs.Trigger.Label>Explore</NativeTabs.Trigger.Label>
        <NativeTabs.Trigger.Icon sf="safari" md="explore" />
      </NativeTabs.Trigger>

      <NativeTabs.Trigger name="settings">
        <NativeTabs.Trigger.Label>Settings</NativeTabs.Trigger.Label>
        <NativeTabs.Trigger.Icon sf="gear" md="settings" />
      </NativeTabs.Trigger>
    </NativeTabs>
  )
}

Each Trigger's name matches a file in the folder. sf is the SF Symbol used on iOS and md is the Material Symbol used on Android, so you get platform-correct icons with no icon library.

On SDK 55–57, import from expo-router/unstable-native-tabs instead. The component API is the same.

Different icons for selected and unselected

Pass an object to switch icons based on state, for example an outline icon that fills in when the tab is active:

<NativeTabs.Trigger.Icon
  sf={{ default: "bell", selected: "bell.fill" }}
  md="notifications"
/>

Add a badge

Show unread counts with a badge. It's native, so it matches the system style exactly:

<NativeTabs.Trigger name="inbox">
  <NativeTabs.Trigger.Label>Inbox</NativeTabs.Trigger.Label>
  <NativeTabs.Trigger.Icon sf="tray.fill" md="inbox" />
  <NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>
</NativeTabs.Trigger>

A dedicated search tab (iOS 26+)

On iOS 26 and later, a tab with role="search" is shown as a separate search button next to the Liquid Glass bar, the same pattern Apple's own apps use:

<NativeTabs.Trigger name="search" role="search">
  <NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>

Minimize the tab bar on scroll

Liquid Glass tab bars can shrink while the user scrolls content, which gives your screen more room:

<NativeTabs minimizeBehavior="onScrollDown">
  {/* triggers */}
</NativeTabs>

Hide, disable, and tune tab behavior

  • hidden removes a tab from the bar entirely. This is handy for feature flags or role-based tabs.
  • disabled blocks taps on the tab but still lets you navigate there from code.
  • disablePopToTop and disableScrollToTop turn off the default "tap the active tab to reset" behavior.
  • tabBarRespectsIMEInsets lifts the Android tab bar above the keyboard.
<NativeTabs.Trigger name="admin" hidden={!user.isAdmin}>
  <NativeTabs.Trigger.Label>Admin</NativeTabs.Trigger.Label>
  <NativeTabs.Trigger.Icon sf="lock.shield" md="admin_panel_settings" />
</NativeTabs.Trigger>

Stacks inside tabs

Most real apps need a stack inside each tab (list → detail). Make the tab a folder with its own _layout.tsx:

app/(tabs)/
  _layout.tsx        // NativeTabs
  home/
    _layout.tsx      // Stack
    index.tsx
    [id].tsx
// app/(tabs)/home/_layout.tsx
import { Stack } from "expo-router"

export default function HomeStack() {
  return <Stack />
}

The Trigger's name becomes home. For more on organizing routes like this, see our Expo Router folder structure guide.

When you need a custom tab bar instead

The system bar is deliberately limited. You can't put a floating action button in the middle, use a gradient background, or animate a pill indicator. If your design calls for that, use the JavaScript Tabs component with a custom tabBar.

Our free Animated Bottom Navigation component ships 11 styles behind one variant prop, including Glassmorphism, Floating, Center FAB, Curved, and an iOS-style Dock. Every animation runs on the native driver, so you get the custom look without dropped frames.

Next steps

Frequently asked questions

What are native tabs in Expo Router?

Native tabs (NativeTabs) render the platform's real system tab bar — UITabBarController on iOS with Liquid Glass on iOS 26+, and a Material bottom navigation bar on Android — instead of a tab bar drawn in JavaScript. You get native animations, accessibility, and system behaviors like scroll-to-top for free.

How do I import NativeTabs in Expo SDK 58?

On SDK 58 and later, import it from `expo-router/native-tabs`. On SDK 55 to 57 the same API lived at `expo-router/unstable-native-tabs`, so update the import when you upgrade.

Should I use NativeTabs or the JavaScript Tabs component?

Use NativeTabs when you want the system look (Liquid Glass on iOS) and the best performance. Use JavaScript Tabs or a custom tab bar when you need full visual control — gradients, a floating center button, custom animations — that the system tab bar does not allow.

Do native tabs work in Expo Go?

Yes, native tabs are part of Expo Router and work in Expo Go and development builds. Liquid Glass only appears on devices running iOS 26 or later, and older iOS versions show the classic system tab bar.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates