@atom_design/tabs
v1.0.0
Published
A flexible React Native Tabs component with multiple style variants, sizes, badges, and scrollable support.
Readme
@atom_design/tabs
A highly customizable, accessible React Native Tabs component with multiple style variants, sizes, badges, icons, and scrollable support.
Features
- 🎨 6 Variants: allRadius, leftRightRadius, noRadius, roundedRadius, dualLineTopRadius, underline
- 📏 3 Sizes: small, medium, large
- 🔢 Badges: Built-in badge support with custom rendering
- 🎯 Icons: Custom icon support for each tab
- 📜 Scrollable: Horizontal scroll for many tabs
- ♿ Accessible: Full accessibility support
- 🎯 TypeScript: Full TypeScript definitions included
- 🎨 Customizable: Style every part of the tabs
Installation
npm install @atom_design/tabs
# or
yarn add @atom_design/tabsPeer Dependencies
npm install react react-native prop-typesUsage
Basic Usage
import React, { useState } from 'react';
import Tabs from '@atom_design/tabs';
const App = () => {
const [activeTab, setActiveTab] = useState('1');
const tabs = [
{ id: '1', name: 'Home' },
{ id: '2', name: 'Profile' },
{ id: '3', name: 'Settings' },
];
return (
<Tabs
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
/>
);
};Variants
// All corners rounded (default)
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="allRadius" />
// Only first and last tabs rounded
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="leftRightRadius" />
// No border radius
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="noRadius" />
// Fully rounded pill shape
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="roundedRadius" />
// Top corners rounded
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="dualLineTopRadius" />
// Underline style
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} variant="underLineTabBackground" />Sizes
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} size="small" />
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} size="medium" />
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} size="large" />With Badges
const tabsWithBadges = [
{ id: '1', name: 'Inbox', badge: 5 },
{ id: '2', name: 'Updates', badge: 12 },
{ id: '3', name: 'Archive', badge: 0 },
];
<Tabs
tabs={tabsWithBadges}
activeTab={activeTab}
onTabChange={setActiveTab}
showBadge={true}
/>With Icons
import Icon from 'react-native-vector-icons/MaterialIcons';
<Tabs
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
renderIcon={(tab, isActive) => (
<Icon
name={tab.icon}
size={18}
color={isActive ? '#fff' : '#666'}
/>
)}
/>Scrollable Tabs
const manyTabs = [
{ id: '1', name: 'Tab 1' },
{ id: '2', name: 'Tab 2' },
{ id: '3', name: 'Tab 3' },
{ id: '4', name: 'Tab 4' },
{ id: '5', name: 'Tab 5' },
{ id: '6', name: 'Tab 6' },
];
<Tabs
tabs={manyTabs}
activeTab={activeTab}
onTabChange={setActiveTab}
scrollable={true}
/>Custom Colors
<Tabs
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
activeColor="#007AFF"
activeBackgroundColor="#007AFF"
inactiveColor="#888"
inactiveBackgroundColor="#e0e0e0"
/>Custom Styling
<Tabs
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
containerStyle={{ borderRadius: 12, padding: 6 }}
tabStyle={{ paddingVertical: 14 }}
activeTabStyle={{ shadowOpacity: 0.2 }}
textStyle={{ fontWeight: '500' }}
activeTextStyle={{ fontWeight: 'bold' }}
/>Disabled State
// Disable all tabs
<Tabs tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} disabled={true} />
// Disable individual tabs
const tabsWithDisabled = [
{ id: '1', name: 'Active' },
{ id: '2', name: 'Disabled', disabled: true },
{ id: '3', name: 'Active' },
];
<Tabs tabs={tabsWithDisabled} activeTab={activeTab} onTabChange={setActiveTab} />Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| tabs | TabItem[] | [] | Array of tab items (required) |
| activeTab | string \| number | - | Currently active tab ID |
| onTabChange | (tabId) => void | - | Callback when tab changes |
| variant | TabVariant | 'allRadius' | Visual style variant |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Tab size |
| disabled | boolean | false | Disable all tabs |
| scrollable | boolean | false | Enable horizontal scrolling |
| showBadge | boolean | false | Show badges on tabs |
| activeColor | string | '#d9232d' | Active text/border color |
| inactiveColor | string | '#666' | Inactive text color |
| activeBackgroundColor | string | '#d9232d' | Active background color |
| inactiveBackgroundColor | string | '#f0f0f0' | Container background color |
| containerStyle | StyleProp<ViewStyle> | - | Container style |
| tabStyle | StyleProp<ViewStyle> | - | Each tab style |
| activeTabStyle | StyleProp<ViewStyle> | - | Active tab style |
| textStyle | StyleProp<TextStyle> | - | Text style |
| activeTextStyle | StyleProp<TextStyle> | - | Active text style |
| badgeStyle | StyleProp<ViewStyle> | - | Badge style |
| renderIcon | (tab, isActive) => ReactNode | - | Custom icon renderer |
| renderBadge | (tab, isActive) => ReactNode | - | Custom badge renderer |
| testID | string | - | Test ID for testing |
TabItem Interface
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| id | string \| number | Yes | Unique identifier |
| name | string | Yes | Display name |
| badge | string \| number | No | Badge value |
| disabled | boolean | No | Disable this tab |
| accessibilityLabel | string | No | Custom a11y label |
Available Variants
| Variant | Description |
|---------|-------------|
| allRadius | All tabs have rounded corners (default) |
| leftRightRadius | Only first and last tabs have rounded corners |
| noRadius | No border radius on tabs |
| roundedRadius | Fully rounded pill-shaped tabs |
| dualLineTopRadius | Top corners rounded, good for card headers |
| underLineTabBackground | Underline-style tabs with bottom indicator |
Accessibility
The Tabs component is fully accessible:
- Uses
accessibilityRole="tab"for each tab - Uses
accessibilityRole="tablist"for container - Announces selected state via
accessibilityState - Reports disabled state
- Provides position hint ("Tab 1 of 3")
Full Test Screen Example
Copy this complete screen to test all tabs variations in your app:
import React, { useState } from 'react';
import { ScrollView, View, Text, StyleSheet } from 'react-native';
import Tabs from '@atom_design/tabs';
// Optional: for custom icons
// import Icon from 'react-native-vector-icons/MaterialIcons';
const TabsTestScreen = () => {
const [activeTab1, setActiveTab1] = useState('1');
const [activeTab2, setActiveTab2] = useState('1');
const [activeTab3, setActiveTab3] = useState('1');
const [activeTab4, setActiveTab4] = useState('1');
const [activeTab5, setActiveTab5] = useState('1');
const [activeTab6, setActiveTab6] = useState('1');
const [activeTab7, setActiveTab7] = useState('1');
const [activeTab8, setActiveTab8] = useState('1');
const [activeTab9, setActiveTab9] = useState('1');
const [activeTab10, setActiveTab10] = useState('1');
const [activeTab11, setActiveTab11] = useState('1');
const [activeTab12, setActiveTab12] = useState('1');
// Basic tabs
const basicTabs = [
{ id: '1', name: 'Home' },
{ id: '2', name: 'Profile' },
{ id: '3', name: 'Settings' },
];
// Tabs with badges
const badgeTabs = [
{ id: '1', name: 'Inbox', badge: 5 },
{ id: '2', name: 'Updates', badge: 23 },
{ id: '3', name: 'Spam', badge: 100 },
];
// Many tabs for scrollable
const manyTabs = [
{ id: '1', name: 'Overview' },
{ id: '2', name: 'Analytics' },
{ id: '3', name: 'Reports' },
{ id: '4', name: 'Users' },
{ id: '5', name: 'Settings' },
{ id: '6', name: 'Help' },
];
// Tabs with disabled
const mixedTabs = [
{ id: '1', name: 'Active' },
{ id: '2', name: 'Disabled', disabled: true },
{ id: '3', name: 'Active' },
];
// Two tabs
const twoTabs = [
{ id: '1', name: 'Option A' },
{ id: '2', name: 'Option B' },
];
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.title}>@atom_design/tabs</Text>
<Text style={styles.subtitle}>Complete Component Test</Text>
{/* VARIANTS SECTION */}
<Text style={styles.sectionTitle}>Variants</Text>
<Text style={styles.label}>All Radius (Default)</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab1}
onTabChange={setActiveTab1}
variant="allRadius"
/>
<Text style={styles.label}>Left Right Radius</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab2}
onTabChange={setActiveTab2}
variant="leftRightRadius"
/>
<Text style={styles.label}>No Radius</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab3}
onTabChange={setActiveTab3}
variant="noRadius"
/>
<Text style={styles.label}>Rounded Radius (Pill)</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab4}
onTabChange={setActiveTab4}
variant="roundedRadius"
/>
<Text style={styles.label}>Dual Line Top Radius</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab5}
onTabChange={setActiveTab5}
variant="dualLineTopRadius"
/>
<Text style={styles.label}>Underline Style</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab6}
onTabChange={setActiveTab6}
variant="underLineTabBackground"
/>
{/* SIZES SECTION */}
<Text style={styles.sectionTitle}>Sizes</Text>
<Text style={styles.label}>Small</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab7}
onTabChange={setActiveTab7}
size="small"
/>
<Text style={styles.label}>Medium (Default)</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab7}
onTabChange={setActiveTab7}
size="medium"
/>
<Text style={styles.label}>Large</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab7}
onTabChange={setActiveTab7}
size="large"
/>
{/* BADGES SECTION */}
<Text style={styles.sectionTitle}>With Badges</Text>
<Tabs
tabs={badgeTabs}
activeTab={activeTab8}
onTabChange={setActiveTab8}
showBadge={true}
/>
<Text style={styles.label}>Underline with Badges</Text>
<Tabs
tabs={badgeTabs}
activeTab={activeTab8}
onTabChange={setActiveTab8}
variant="underLineTabBackground"
showBadge={true}
/>
{/* SCROLLABLE SECTION */}
<Text style={styles.sectionTitle}>Scrollable Tabs</Text>
<Tabs
tabs={manyTabs}
activeTab={activeTab9}
onTabChange={setActiveTab9}
scrollable={true}
/>
{/* CUSTOM COLORS SECTION */}
<Text style={styles.sectionTitle}>Custom Colors</Text>
<Text style={styles.label}>Blue Theme</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab10}
onTabChange={setActiveTab10}
activeColor="#007AFF"
activeBackgroundColor="#007AFF"
inactiveBackgroundColor="#e8f0fe"
/>
<Text style={styles.label}>Green Theme</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab10}
onTabChange={setActiveTab10}
activeColor="#34C759"
activeBackgroundColor="#34C759"
inactiveBackgroundColor="#e8f8ec"
/>
<Text style={styles.label}>Purple Underline</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab10}
onTabChange={setActiveTab10}
variant="underLineTabBackground"
activeColor="#AF52DE"
/>
{/* DISABLED SECTION */}
<Text style={styles.sectionTitle}>Disabled States</Text>
<Text style={styles.label}>Mixed Enabled/Disabled</Text>
<Tabs
tabs={mixedTabs}
activeTab={activeTab11}
onTabChange={setActiveTab11}
/>
<Text style={styles.label}>All Disabled</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab11}
onTabChange={setActiveTab11}
disabled={true}
/>
{/* CUSTOM STYLING SECTION */}
<Text style={styles.sectionTitle}>Custom Styling</Text>
<Text style={styles.label}>Custom Container & Tabs</Text>
<Tabs
tabs={basicTabs}
activeTab={activeTab12}
onTabChange={setActiveTab12}
containerStyle={styles.customContainer}
tabStyle={styles.customTab}
activeTabStyle={styles.customActiveTab}
textStyle={styles.customText}
activeTextStyle={styles.customActiveText}
/>
<Text style={styles.label}>Rounded with Shadow</Text>
<Tabs
tabs={twoTabs}
activeTab={activeTab12}
onTabChange={setActiveTab12}
variant="roundedRadius"
containerStyle={styles.shadowContainer}
activeTabStyle={styles.shadowActiveTab}
/>
{/* TWO TABS SECTION */}
<Text style={styles.sectionTitle}>Two Tabs (Toggle Style)</Text>
<Tabs
tabs={twoTabs}
activeTab={activeTab12}
onTabChange={setActiveTab12}
variant="roundedRadius"
/>
<View style={{ height: 50 }} />
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
content: {
padding: 20,
},
title: {
fontSize: 28,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 5,
color: '#333',
},
subtitle: {
fontSize: 16,
textAlign: 'center',
marginBottom: 30,
color: '#666',
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
marginTop: 30,
marginBottom: 15,
color: '#333',
borderBottomWidth: 1,
borderBottomColor: '#ddd',
paddingBottom: 5,
},
label: {
fontSize: 14,
color: '#666',
marginBottom: 8,
marginTop: 15,
},
// Custom styling examples
customContainer: {
borderRadius: 12,
padding: 6,
backgroundColor: '#1a1a2e',
},
customTab: {
paddingVertical: 12,
},
customActiveTab: {
backgroundColor: '#e94560',
borderRadius: 10,
},
customText: {
color: '#888',
fontWeight: '500',
},
customActiveText: {
color: '#fff',
fontWeight: 'bold',
},
shadowContainer: {
backgroundColor: '#fff',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
shadowActiveTab: {
shadowColor: '#d9232d',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 4,
},
});
export default TabsTestScreen;Usage in Your App
// App.js or navigation screen
import TabsTestScreen from './TabsTestScreen';
// In your navigator or directly
<TabsTestScreen />License
MIT © Atom Design
