Overview of expo-linear-like-bottom-tabs
mainreact-native-reanimated, expo, and react-native-gesture-handler. It is cross-platform, supporting both iOS and Android, and is fully typed with TypeScript.repository·main·Indexed 42 days ago
https://github.com/rit3zh/expo-linear-like-bottom-tabsA React Native component library providing a smooth, minimal, floating bottom tab bar inspired by the Linear app interface. Built with react-native-reanimated, expo, and react-native-gesture-handler, it offers a gesture-driven interface for @react-navigation/bottom-tabs including the LinearTabBar component and support for expanded menu items.
react-native-reanimated, expo, and react-native-gesture-handler. It is cross-platform, supporting both iOS and Android, and is fully typed with TypeScript.To use this project, clone the repository, install dependencies using bun, and run the platform-specific commands to start the development server for iOS or Android.
git clone https://github.com/rit3zh/expo-linear-like-bottom-tabs
cd expo-linear-like-bottom-tabs
bun install
bun ios
bun androidThe LinearTabBar component is a custom tab bar designed for use with @react-navigation/bottom-tabs. It provides a highly animated, gesture-driven interface that can expand into a menu.
To use it, pass it to the tabBar prop of your BottomTabNavigator.
| Prop | Type | Description |
|---|---|---|
state | BottomTabNavigationState | Required. The navigation state provided by the navigator. |
descriptors | BottomTabDescriptorMap | Required. Descriptors for each route. |
navigation | BottomTabNavigationProp | Required. The navigation object. |
onLinearTabPress (optional) | () => void | any | Callback triggered when the special 'Linear' tab (the dummy tab) is pressed. |
onMenuItemPress (optional) | (index: number) => void | any | Callback triggered when an item in the expanded menu is pressed. |
import { LinearTabBar } from '@/components/linear-tab-bar';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator
tabBar={(props) => (
<LinearTabBar
{...props}
onLinearTabPress={() => console.log('Linear tab pressed!')}
onMenuItemPress={(index) => console.log('Menu item pressed:', index)}
/>
)}
>
{/* Your Tab.Screen components here */}
</Tab.Navigator>
);
}The package exports all components and types from ./linear-tab-bar. You can import the primary tab bar component and its associated types directly from the package root.
import { LinearTabBar } from 'expo-linear-like-bottom-tabs';The library provides specific interfaces for data items that might be displayed within the tab interface:
FavoriteItem: Represents a favorite entry with id, title, checked status, and an optional icon (which can be "loading", "clock", or null).TeamItem: Represents a team entry with id, name, and an optional icon string.interface FavoriteItem {
id: string;
title: string;
checked: boolean;
icon?: "loading" | "clock" | null;
}
interface TeamItem {
id: string;
name: string;
icon?: string;
}The ExpandedMenuItemProps interface is used to pass state and handlers to individual items within the expanded menu view, including the current animationProgress and the totalItems count for layout calculations.
import type { SharedValue } from "react-native-reanimated";
import { ExpandedMenuItem } from "./typings";
interface ExpandedMenuItemProps {
item: ExpandedMenuItem;
index: number;
animationProgress: SharedValue<number>;
totalItems: number;
isSelected: boolean;
onPress: () => void;
}The HapticFeedBackWeight type defines the allowed intensity levels for haptic feedback interactions within the component.
type HapticFeedBackWeight = "soft" | "normal" | "heavy";When building custom menu items for the expanded state of the bottom tabs, use the ExpandedMenuItem interface. It requires an iconName that must be a valid key from @expo/vector-icons's Ionicons.glyphMap, a label string, and a route string representing the navigation path.
import { Ionicons } from "@expo/vector-icons";
interface ExpandedMenuItem {
iconName: keyof typeof Ionicons.glyphMap;
label: string;
route: string;
}The AnimatedTabProps interface defines the properties required for an animated tab component. It uses react-native-reanimated's SharedValue<number> to drive the animationProgress.
import type { SharedValue } from "react-native-reanimated";
interface AnimatedTabProps {
isFocused: boolean;
options: any;
colors: {
primary: string;
text: string;
};
onPress: () => void;
onLongPress: () => void;
animationProgress: SharedValue<number>;
index: number;
}