Bottom Tabs
Live Preview
Bottom tab navigation is the primary navigation pattern for all RetailOS mobile applications. It provides thumb-friendly access to top-level screens.
Anatomy
+------------------------------------------------+
| |
| (Screen Content) |
| |
+--------------------------------------------------+
| ┌──────────────────────────────────────────┐ |
| │ [icon] [icon] [icon] [icon] │ | <- Tab bar
| │ Beranda Stok Tugas Lainnya │ | <- Labels
| └──────────────────────────────────────────┘ |
| (Safe Area Padding) |
+--------------------------------------------------+Specifications
| Property | Value |
|---|---|
| Total height | 84px (includes safe area inset) |
| Tab bar height | 50px (content area) |
| Safe area bottom | 34px (iPhone) / varies (Android) |
| Background | bg-card (#FFFFFF / dark: #262420) |
| Top border | 1px solid border (#E5E2DB / dark: #3D3A35) |
| Max tabs | 5 |
| Min tabs | 3 |
Tab Item
| Property | Value |
|---|---|
| Icon size | 24px |
| Label size | 10px |
| Label font | Plus Jakarta Sans, weight 500 |
| Gap (icon to label) | 4px |
| Active color | brand (#2D6B4A / dark: #4A9E72) |
| Inactive color | text-3 (#A8A29E / dark: #6E6A64) |
React Native Implementation
Use platform-native tab components where available for the best performance and native feel.
tsx
import { Tabs } from 'expo-router';
import { Home, Package, ClipboardList, MoreHorizontal } from 'lucide-react-native';
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#2D6B4A',
tabBarInactiveTintColor: '#A8A29E',
tabBarStyle: {
backgroundColor: '#FFFFFF',
borderTopColor: '#E5E2DB',
borderTopWidth: 1,
height: 84,
paddingBottom: 34, // safe area
paddingTop: 8,
},
tabBarLabelStyle: {
fontFamily: 'PlusJakartaSans-Medium',
fontSize: 10,
},
}}
>
<Tabs.Screen
name="index"
options={{
title: 'Beranda',
tabBarIcon: ({ color }) => <Home size={24} color={color} />,
}}
/>
<Tabs.Screen
name="stock"
options={{
title: 'Stok',
tabBarIcon: ({ color }) => <Package size={24} color={color} />,
}}
/>
<Tabs.Screen
name="tasks"
options={{
title: 'Tugas',
tabBarIcon: ({ color }) => <ClipboardList size={24} color={color} />,
}}
/>
<Tabs.Screen
name="more"
options={{
title: 'Lainnya',
tabBarIcon: ({ color }) => <MoreHorizontal size={24} color={color} />,
}}
/>
</Tabs>
);
}Per-App Tab Configurations
Stock App (Warehouse / Store Staff)
| Tab | Icon | Label |
|---|---|---|
| 1 | Home | Beranda |
| 2 | Package | Stok |
| 3 | ClipboardList | Tugas |
| 4 | MoreHorizontal | Lainnya |
Customer App
| Tab | Icon | Label |
|---|---|---|
| 1 | Home | Beranda |
| 2 | Gift | Promo |
| 3 | Receipt | Riwayat |
| 4 | User | Profil |
Store Admin (Mobile Web)
| Tab | Icon | Label |
|---|---|---|
| 1 | LayoutDashboard | Dashboard |
| 2 | Banknote | Keuangan |
| 3 | Package | Inventori |
| 4 | Users | Staf |
| 5 | Settings | Pengaturan |
Badge on Tab
For notification indicators, use a small dot or count badge.
tsx
<Tabs.Screen
name="tasks"
options={{
title: 'Tugas',
tabBarIcon: ({ color }) => <ClipboardList size={24} color={color} />,
tabBarBadge: 3, // Shows count
tabBarBadgeStyle: {
backgroundColor: '#C4463A',
fontSize: 10,
fontWeight: '600',
minWidth: 18,
},
}}
/>DO and DON'T
DO
- Use platform-native tab components (expo-router Tabs, NativeTabs) for native feel
- Keep tab labels short --- one word (Indonesian) is ideal
- Use the brand green for active tab color
- Include safe area padding for notched devices
DON'T
- Don't exceed 5 tabs --- use a "Lainnya" (More) tab for additional screens
- Don't use text-only tabs without icons
- Don't animate tab switches --- content should swap instantly
- Don't use the tab bar for contextual actions (use a floating action button or action bar instead)