Expokit
AboutDocsComponentsChartsBlocksPricing
Sign inGet started
OverviewGlassProGlass ButtonProGlass Nav BarProGlass Search BarProGlass Segmented ControlProGlass SheetProGlass Tab BarProNative bottom tabs (Liquid Glass)
Native bottom tabs (Liquid Glass)
Sponsor ExpoKit

Free stays free. Tip once — keep shipping.

Support
About
Docs
IntroductionWhat ExpoKit is and how it worksInstallationAdd ExpoKit to an Expo appThemingColours, radius and dark modeCLIinit, add, login and updateAI assistants (MCP)Cursor, Claude, Antigravity and moreRun on your phoneTry components in Expo GoChangelogWhat shipped recently
npx @expokit/cli@latest initQuick start
Components
Liquid Glass8 iOS 26 glass pieces3DNew14 tilt, flip and stack
Inputs 12
  • CalendarNew
  • Date PickerNew
  • Floating Input
  • OTP Input
  • Password Input
  • +7 more
Selection 7
  • Checkbox
  • Chip Group
  • Color SwatchPro
  • Radio Group
  • RatingPro
  • +2 more
Buttons 7
  • Button
  • Floating Action ButtonPro
  • Like ButtonPro
  • Morph ButtonPro
  • Shimmer ButtonPro
  • +2 more
Feedback 5
  • Alert
  • Progress
  • Skeleton
  • Spinner
  • Toast
Overlays 4
  • Action SheetNew
  • Bottom Sheet
  • Dialog
  • DrawerNew
Display 9
  • Accordion
  • Avatar
  • Badge
  • Card
  • CarouselPro
  • +4 more
Navigation 3
  • Bottom NavNew
  • Progress StepsNew
  • Tabs
Motion 2
  • Number TickerPro
  • Text Animation
70 components · 347 live variantsAll components
Charts

Cartesian

  • Area Chart
  • Bar Chart
  • Line Chart
  • Combo Chart

Circular

  • Pie & Donut
  • Radial Chart
  • Radar Chart
  • Polar AreaPro

3D & Pixel

  • Isometric BarsPro
  • Pixel BarsPro

Specialty

  • TreemapPro
  • WaterfallPro
  • BulletPro
  • TimelinePro
  • Gauge
  • Candlestick
  • Heatmap
  • Funnel

Classic

  • Sparkline
  • Progress Rings
  • MultiLine (classic)
  • StackedBar (classic)
  • Ranking Bars (classic)

Guides

  • Usage & config
  • Patterns & effects
  • Show / hide anything
  • Chart theming
  • Interactions
23 charts, built on the open-source expo-charts engineAll charts
Blocks

Screens

ChatProAn AI assistant chat screenCheckoutProA premium checkoutDashboardProA finance / analytics home screenInboxProA messaging inboxOnboardingProA minimal swipeable welcome flowProfileProAccount profile for any appSearch ResultsProA clean, monochrome search screenSettingsProAn iOS-style grouped settings screenSign InProOne configurable sign-in flow modelled on top mobile apps
9 complete screens — drop one in, wire your dataSee them on devices All blocks
Pricing
DocsGlassNative bottom tabs (Liquid Glass)

Native bottom tabs (Liquid Glass)

Real Liquid Glass NativeTabs on iOS 26+, and a floating blur glass tab bar with the same scroll-collapse on iOS 18 and older, Android and web.

Expo SDK 55+Expo Router11 files
9:41

Installation#

Run it in the root of an Expo Router app. It copies the files, creates a route for every tab and installs the dependencies.

Works on every iOS version#

Real Liquid Glass only exists on iOS 26 (and the app must be built with Xcode 26). On anything older the system tab bar is the plain iOS 18 bar — so the template checks isLiquidGlassAvailable() at launch and picks the best bar for the device:

iOS 26+
System NativeTabs with real Liquid Glass
Native minimize on scroll, search role, SF Symbols
iOS 18 and older
Floating blur glass pill (GlassTabBar)
Same collapse-into-a-circle on scroll, sliding selection bubble
Android
Floating blur glass pill (or Material bar)
Set TAB_BAR.legacy = 'native' for the system bar
Web
Floating glass pill
Works in the browser and Expo Go
PlatformTab barNotes
iOS 26+System NativeTabs with real Liquid GlassNative minimize on scroll, search role, SF Symbols
iOS 18 and olderFloating blur glass pill (GlassTabBar)Same collapse-into-a-circle on scroll, sliding selection bubble
AndroidFloating blur glass pill (or Material bar)Set TAB_BAR.legacy = 'native' for the system bar
WebFloating glass pillWorks in the browser and Expo Go
Upgrading from an older version of this template? SDK 55 moved the tab icon API to NativeTabs.Trigger.Icon / .Label / .Badge, and SDK 56 moved navigation themes into expo-router. Run npx @expokit/cli@latest update to get the fixed files — your tabs.ts is kept.

Configure your tabs#

Everything lives in src/lib/tabs.ts: labels, SF Symbols for iOS, vector icons for Android and the fallback bar, badges and colours. Add a tab there and run npx @expokit/cli@latest update to create its route.

src/lib/tabs.ts
export const TAB_BAR = {  minimizeBehavior: 'onScrollDown',   // 'never' | 'onScrollUp' | 'automatic'  legacy: 'glass',                    // 'native' = system tab bar below iOS 26  labelVisibilityMode: 'labeled',  colors: {    tint: { light: '#007AFF', dark: '#0A84FF' },    inactive: { light: 'rgba(60,60,67,0.6)', dark: 'rgba(235,235,245,0.6)' },  },}; export const TABS: TabItem[] = [  {    name: 'index',    title: 'Home',    sf: { default: 'house', selected: 'house.fill' },          // iOS    icon: { set: 'Ionicons', name: 'home-outline', selected: 'home' }, // Android, web, fallback  },  { name: 'alerts', title: 'Alerts', badge: '3', sf: 'bell', icon: { set: 'Feather', name: 'bell' } },];

Scroll to collapse#

iOS 26 collapses the bar natively. For the glass fallback, pass useTabBarScroll() to your ScrollView — the bar shrinks into a glass circle holding the active tab, and a tap brings it back.

src/app/(tabs)/feed.tsx
import { ScrollView } from 'react-native';import { useTabBarInset, useTabBarScroll } from '@/lib/tab-bar'; export default function Feed() {  const onScroll = useTabBarScroll();   // collapses the glass bar on older iOS / Android / web  const bottom = useTabBarInset();      // keeps the last row clear of the floating bar  return (    <ScrollView onScroll={onScroll} scrollEventThrottle={16} contentContainerStyle={{ paddingBottom: bottom }}>      {/* … */}    </ScrollView>  );}

Files#

src/app/(tabs)/_layout.tsx
import React from 'react';import { DynamicColorIOS, Platform } from 'react-native';import { Tabs } from 'expo-router/js-tabs';// SDK 54–57 path. On SDK 58+ change this to 'expo-router/native-tabs'.import { NativeTabs } from 'expo-router/unstable-native-tabs'; import { GlassTabBar } from '@/components/GlassTabBar';import { USE_NATIVE_TABS } from '@/lib/tab-bar';import {  asFamily,  resolveIconLibrary,  resolveIconNames,  TAB_BAR,  TABS,  type TabItem,} from '@/lib/tabs'; type IconProps = React.ComponentProps<typeof NativeTabs.Trigger.Icon>; function dynamicColor(pair: { light: string; dark: string }) {  return Platform.OS === 'ios' ? DynamicColorIOS(pair) : pair.light;} /** Vector-font icon rasterised for the native bar (Android, or iOS when no SF Symbol is set). */function vectorSrc(tab: TabItem) {  const library = resolveIconLibrary(tab);  const { name, selected } = resolveIconNames(tab);  if (!library || !name) return undefined;   const family = asFamily(library);  const icon = <NativeTabs.Trigger.VectorIcon family={family} name={name} />;  if (!selected) return icon;  return { default: icon, selected: <NativeTabs.Trigger.VectorIcon family={family} name={selected} /> };} /** * Returns the Icon element itself (not a wrapper component): NativeTabs reads its * children by type, so <NativeTabs.Trigger.Icon> must be a direct child of the Trigger. */function nativeIcon(tab: TabItem, tint: IconProps['selectedColor']) {  const src = tab.src ?? vectorSrc(tab);  if (!tab.sf && !src) return null;  // sf wins on iOS, src is used on Android.  const props = { sf: tab.sf, src, selectedColor: tint } as IconProps;  return <NativeTabs.Trigger.Icon {...props} />;} function NativeTabLayout() {  const tint = dynamicColor(TAB_BAR.colors.tint);  const inactive = dynamicColor(TAB_BAR.colors.inactive);  const { fontSize, fontWeight, selectedFontWeight } = TAB_BAR.labelStyle;   return (    <NativeTabs      tintColor={tint}      iconColor={inactive}      labelVisibilityMode={TAB_BAR.labelVisibilityMode}      minimizeBehavior={TAB_BAR.minimizeBehavior}      labelStyle={{ color: inactive, fontSize, fontWeight }}      {...(TAB_BAR.backgroundColor != null ? { backgroundColor: TAB_BAR.backgroundColor } : null)}    >      {TABS.map((tab) => (        <NativeTabs.Trigger key={tab.name} name={tab.name} hidden={tab.hidden}>          {nativeIcon(tab, tint)}          <NativeTabs.Trigger.Label            hidden={tab.hiddenLabel}            selectedStyle={{ color: tint, fontSize, fontWeight: selectedFontWeight }}          >            {tab.title}          </NativeTabs.Trigger.Label>          {tab.badge ? <NativeTabs.Trigger.Badge>{tab.badge}</NativeTabs.Trigger.Badge> : null}        </NativeTabs.Trigger>      ))}    </NativeTabs>  );} function GlassTabLayout() {  return (    <Tabs      tabBar={(props) => <GlassTabBar {...props} />}      screenOptions={{ headerShown: false, animation: 'fade' }}    >      {TABS.map((tab) => (        <Tabs.Screen          key={tab.name}          name={tab.name}          options={{ title: tab.title, href: tab.hidden ? null : undefined }}        />      ))}    </Tabs>  );} export default function TabLayout() {  return USE_NATIVE_TABS ? <NativeTabLayout /> : <GlassTabLayout />;}