npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-telegram-tabbar

v1.0.39

Published

Native floating tab bar with blur effect — Telegram-style, iOS + Android. Custom rendering: Kotlin Canvas (Android) + CoreGraphics/UIBlurEffect (iOS).

Readme

react-native-telegram-tabbar

Native floating tab bar with frosted glass blur — Telegram-style, for iOS and Android.

  • iOS — UIKit pill with UIBlurEffect, CoreGraphics SVG rendering, SF Symbols
  • Android — Jetpack Compose pill with BlurViewGroup, Lucide drawable icons

Built on Expo Modules API. New Architecture (Fabric) compatible.


Features

  • Floating pill with frosted glass blur (iOS: systemUltraThinMaterialDark, Android: 25f radius)
  • Drop shadow under the pill
  • Active tab — white card background, colored icon and label
  • Inactive tabs — muted icon and label color
  • Bounce animation on tap (scale 1.2 → 0.93 → 1.0)
  • Smooth show/hide via isVisible prop (slide off-screen)
  • Numeric badges and dot badges
  • Long press events
  • Haptic feedback on tap and long press (Android)
  • SF Symbols on iOS (mapped from Lucide icon names)
  • Built-in Lucide icon set (17 icons)
  • Custom SVG icons support
  • 60 FPS, zero allocations on draw path

Requirements

  • React Native 0.73+
  • Expo Modules Core 1.0+
  • @react-navigation/bottom-tabs 7+
  • react-native-safe-area-context 4+

Installation

npm install react-native-telegram-tabbar
# or
yarn add react-native-telegram-tabbar

Rebuild native:

npx expo run:ios
npx expo run:android

Quick Start

// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router'
import { TelegramTabBar } from 'react-native-telegram-tabbar'

export default function TabLayout() {
  return (
    <Tabs
      tabBar={props => <TelegramTabBar {...props} />}
    >
      <Tabs.Screen
        name="index"
        options={{
          tabBarLabel: 'Home',
          tabBarIconName: 'home',
        }}
      />
      <Tabs.Screen
        name="search"
        options={{
          tabBarLabel: 'Search',
          tabBarIconName: 'search',
        }}
      />
      <Tabs.Screen
        name="messages"
        options={{
          tabBarLabel: 'Messages',
          tabBarIconName: 'message',
          tabBarBadge: 3,
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          tabBarLabel: 'Profile',
          tabBarIconName: 'user',
        }}
      />
    </Tabs>
  )
}

Icons

Icons are set via the tabBarIconName screen option. The value is a Lucide icon name.

Built-in icons

| Name | Icon | |------|------| | home | House | | search | Search | | user | User | | plus | CirclePlus | | message | MessageCircle | | bell | Bell | | settings | Settings | | heart | Heart | | login | LogIn | | logout | LogOut | | star | Star | | map | MapPin | | calendar | Calendar | | camera | Camera | | cart | ShoppingCart | | filter | Filter | | menu | Menu |

iOS — SF Symbols

On iOS, Lucide icon names are mapped to native SF Symbols where possible (e.g. homehouse, messagebubble.left.fill). When no mapping exists, the SVG path is rendered via CoreGraphics.

Custom SVG icons

Pass raw SVG path data via tabBarIconName if the built-in set is insufficient, or use the LUCIDE_ICONS / createLucideIconData utilities to build icon data and pass it to the native layer directly if needed for lower-level use.

import { createLucideIconData } from 'react-native-telegram-tabbar'

const arrowIcon = createLucideIconData([
  ['path', { d: 'M5 12h14' }],
  ['path', { d: 'm12 5 7 7-7 7' }],
])

Props

TelegramTabBar

Extends BottomTabBarProps from React Navigation.

| Prop | Type | Default | Description | |------|------|---------|-------------| | theme | TabBarTheme | dark theme | Pill colors | | isVisible | boolean | true | Show/hide with slide animation |

NativeTelegramTabBarView (low-level)

| Prop | Type | Required | Description | |------|------|----------|-------------| | tabs | TabItem[] | Yes | Array of tab data | | activeIndex | number | Yes | Currently active tab index | | theme | TabBarTheme | No | Theme colors | | tabBadges | TabBadgeItem[] | No | Badge items | | bottomInset | number | No | Safe area bottom inset in dp/pt | | isVisible | boolean | No | Show/hide animation | | onTabPress | (e: TabPressEvent) => void | No | Tab press callback | | onTabLongPress | (e: TabLongPressEvent) => void | No | Long press callback |

Screen options

| Option | Type | Description | |--------|------|-------------| | tabBarLabel | string | Tab label text | | tabBarIconName | string | Lucide icon name (see built-in list above) | | tabBarBadge | number \| string | Badge value — number, '99+', or 'dot' for dot badge |

TabBarTheme

interface TabBarTheme {
  backgroundColor: string  // Pill background color
  activeColor: string      // Active tab icon and label color
  inactiveColor: string    // Inactive tab icon and label color
  indicatorColor: string   // Reserved (used on Android indicator)
}

Default theme:

{
  backgroundColor: '#000000',
  activeColor:     '#111111',
  inactiveColor:   '#A9ABB1',
  indicatorColor:  '#111111',
}

Theming

<TelegramTabBar
  {...props}
  theme={{
    backgroundColor: '#FFFFFF',
    activeColor: '#007AFF',
    inactiveColor: '#8E8E93',
    indicatorColor: '#007AFF',
  }}
/>

Dynamic (dark/light mode):

const { colors } = useTheme()

<TelegramTabBar
  {...props}
  theme={{
    backgroundColor: colors.card,
    activeColor: colors.primary,
    inactiveColor: colors.border,
    indicatorColor: colors.primary,
  }}
/>

Badges

Badges are driven by the standard tabBarBadge screen option.

// Numeric badge
<Tabs.Screen options={{ tabBarBadge: 5 }} />

// Capped at 99+
<Tabs.Screen options={{ tabBarBadge: 120 }} />  // shows "99+"

// Dot badge (no number)
<Tabs.Screen options={{ tabBarBadge: 'dot' }} />

Hide on Scroll

Pass isVisible to show/hide the tab bar when the user scrolls.

// hooks/useTabBarScroll.ts
import { useCallback, useRef, useState } from 'react'
import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'

const THRESHOLD = 10

export function useTabBarScroll() {
  const [isVisible, setIsVisible] = useState(true)
  const lastY = useRef(0)

  const onScroll = useCallback((e: NativeSyntheticEvent<NativeScrollEvent>) => {
    const y = e.nativeEvent.contentOffset.y
    const delta = y - lastY.current
    if (y <= 0) setIsVisible(true)
    else if (delta > THRESHOLD) setIsVisible(false)
    else if (delta < -THRESHOLD) setIsVisible(true)
    lastY.current = y
  }, [])

  return { isVisible, onScroll }
}
// _layout.tsx
const { isVisible, onScroll } = useTabBarScroll()

<Tabs tabBar={props => <TelegramTabBar {...props} isVisible={isVisible} />}>
  ...
</Tabs>
// Screen with a list
<FlatList onScroll={onScroll} scrollEventThrottle={16} ... />

Works with FlatList, FlashList, ScrollView.


Long Press

React Navigation emits tabLongPress events — subscribe from any screen:

import { useNavigation } from '@react-navigation/native'

useEffect(() => {
  const unsub = navigation.addListener('tabLongPress', () => {
    // open bottom sheet, etc.
  })
  return unsub
}, [navigation])

Architecture

Expo Router / React Navigation (TypeScript)
    ↓
TelegramTabBar.tsx
  Reads tabBarIconName, tabBarLabel, tabBarBadge from screen options
  Builds tabs[], tabBadges[], resolves activeIndex
    ↓
NativeTelegramTabBarView  (Expo requireNativeView)
    ↓
    ├── Android: TelegramTabBarView.kt
    │     BlurViewGroup (blur pill background)
    │     + Jetpack Compose overlay
    │       ├── Active tab: white rounded card + colored icon + label
    │       ├── Inactive tab: muted icon + label
    │       ├── Badges: red pill (numeric) or red dot
    │       └── Haptic feedback on tap / long press
    │
    └── iOS: TelegramTabBarView.swift
          UIVisualEffectView (.systemUltraThinMaterialDark)
          + UIView overlay
            ├── TabButtonView per tab
            │     SF Symbol (preferred) or CoreGraphics SVG
            │     Active: white rounded card background
            ├── Bounce animation on tap (keyframe)
            └── Slide animation on isVisible change

Platform Support

| Platform | Status | |----------|--------| | Android | Native Kotlin + Jetpack Compose | | iOS | Native Swift + UIKit |


License

MIT