@atom_design/button
v1.0.1
Published
A customizable React Native button component supporting icons, loading states, and multiple variants.
Readme
@atom_design/button
A highly customizable, accessible React Native button component with support for icons, loading states, and multiple variants.
Features
- 🎨 5 Variants: solid, outlined, dashed, text, link
- 📐 5 Types: text, icon, textIcon, circle, square
- 📏 3 Sizes: small, medium, large
- 🔄 Loading State: Built-in loading spinner
- ♿ Accessible: Full accessibility support
- 🎯 TypeScript: Full TypeScript definitions included
- 🔌 Flexible Icons: Works with any icon library
Installation
npm install @atom_design/button
# or
yarn add @atom_design/buttonPeer Dependencies
npm install react react-native prop-types
# Optional: for icons
npm install react-native-vector-iconsUsage
Basic Usage
import Button from '@atom_design/button';
<Button title="Click Me" onPress={() => console.log('Pressed!')} />With Icons
import Button from '@atom_design/button';
import Icon from 'react-native-vector-icons/MaterialIcons';
// Text with left icon
<Button
title="Add Item"
leftIcon="add"
iconComponent={Icon}
onPress={() => {}}
/>
// Text with right icon
<Button
title="Next"
rightIcon="arrow-forward"
iconComponent={Icon}
onPress={() => {}}
/>
// Icon only (circle)
<Button
type="circle"
icon="add"
iconComponent={Icon}
onPress={() => {}}
/>Variants
// Solid (default)
<Button title="Solid" variant="solid" />
// Outlined
<Button title="Outlined" variant="outlined" />
// Dashed
<Button title="Dashed" variant="dashed" />
// Text (no background)
<Button title="Text" variant="text" />
// Link (underlined)
<Button title="Link" variant="link" />Sizes
<Button title="Small" size="small" />
<Button title="Medium" size="medium" />
<Button title="Large" size="large" />Types
// Text only (default)
<Button title="Text" type="text" />
// Text with icons
<Button title="Text Icon" type="textIcon" leftIcon="star" iconComponent={Icon} />
// Icon only
<Button type="icon" icon="settings" iconComponent={Icon} />
// Circle icon button
<Button type="circle" icon="add" iconComponent={Icon} />
// Square icon button
<Button type="square" icon="menu" iconComponent={Icon} />Loading State
<Button title="Loading..." loading={true} onPress={() => {}} />Disabled State
<Button title="Disabled" disabled={true} onPress={() => {}} />Custom Colors
<Button
title="Custom Colors"
color="#007AFF" // Primary color
textColor="#ffffff" // Text color
disabledColor="#cccccc" // Background when disabled
disabledTextColor="#999" // Text when disabled
/>Custom Styling
<Button
title="Custom Style"
style={{ marginVertical: 10, shadowOpacity: 0.3 }}
textStyle={{ fontWeight: 'bold', letterSpacing: 1 }}
borderRadius={20}
/>Custom Icon Component
You can use any icon library that follows the standard icon component interface:
import { Ionicons } from '@expo/vector-icons';
<Button
title="With Ionicons"
leftIcon="heart"
iconComponent={Ionicons}
/>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | string | 'Button' | Button text |
| onPress | () => void | - | Press handler |
| leftIcon | string | - | Icon name for left side |
| rightIcon | string | - | Icon name for right side |
| icon | string | - | Icon for icon-only buttons |
| disabled | boolean | false | Disable the button |
| loading | boolean | false | Show loading spinner |
| size | 'small' \| 'medium' \| 'large' | 'medium' | Button size |
| variant | 'solid' \| 'outlined' \| 'dashed' \| 'text' \| 'link' | 'solid' | Visual style |
| type | 'text' \| 'icon' \| 'textIcon' \| 'circle' \| 'square' | 'text' | Button type |
| color | string | '#d9232d' | Primary color |
| textColor | string | - | Text color |
| disabledColor | string | '#d7d7dc' | Disabled background |
| disabledTextColor | string | '#fff' | Disabled text color |
| borderRadius | number | 6 | Border radius |
| iconComponent | ComponentType | - | Custom icon component |
| iconSize | number | - | Custom icon size |
| style | StyleProp<ViewStyle> | - | Container styles |
| textStyle | StyleProp<TextStyle> | - | Text styles |
| accessibilityLabel | string | title | Accessibility label |
| accessibilityHint | string | - | Accessibility hint |
| testID | string | - | Test ID |
Accessibility
The Button component is fully accessible:
- Uses
accessibilityRole="button" - Automatically sets
accessibilityLabelfromtitle(can be overridden) - Supports
accessibilityHintfor additional context - Correctly reports disabled state via
accessibilityState
Size Reference
| Size | Button Padding | Font Size | Icon Size | Circle/Square Size | |------|---------------|-----------|-----------|-------------------| | small | 8x10 | 12 | 16 | 32x32 | | medium | 12x14 | 14 | 20 | 40x40 | | large | 16x18 | 16 | 24 | 48x48 |
Full Test Screen Example
Copy this complete screen to test all button variations in your app:
import React, { useState } from 'react';
import { ScrollView, View, Text, StyleSheet, Alert } from 'react-native';
import Button from '@atom_design/button';
import Icon from 'react-native-vector-icons/MaterialIcons';
const ButtonTestScreen = () => {
const [loading, setLoading] = useState(false);
const handlePress = (buttonName) => {
Alert.alert('Button Pressed', `You pressed: ${buttonName}`);
};
const handleLoadingPress = () => {
setLoading(true);
setTimeout(() => setLoading(false), 2000);
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.title}>@atom_design/button</Text>
<Text style={styles.subtitle}>Complete Component Test</Text>
{/* VARIANTS SECTION */}
<Text style={styles.sectionTitle}>Variants</Text>
<View style={styles.row}>
<Button
title="Solid"
variant="solid"
onPress={() => handlePress('Solid')}
/>
<Button
title="Outlined"
variant="outlined"
onPress={() => handlePress('Outlined')}
/>
</View>
<View style={styles.row}>
<Button
title="Dashed"
variant="dashed"
onPress={() => handlePress('Dashed')}
/>
<Button
title="Text"
variant="text"
onPress={() => handlePress('Text')}
/>
<Button
title="Link"
variant="link"
onPress={() => handlePress('Link')}
/>
</View>
{/* SIZES SECTION */}
<Text style={styles.sectionTitle}>Sizes</Text>
<View style={styles.row}>
<Button
title="Small"
size="small"
onPress={() => handlePress('Small')}
/>
<Button
title="Medium"
size="medium"
onPress={() => handlePress('Medium')}
/>
<Button
title="Large"
size="large"
onPress={() => handlePress('Large')}
/>
</View>
{/* TYPES SECTION */}
<Text style={styles.sectionTitle}>Types</Text>
<View style={styles.row}>
<Button
title="Text Only"
type="text"
onPress={() => handlePress('Text Only')}
/>
<Button
title="Text + Icon"
type="textIcon"
leftIcon="star"
iconComponent={Icon}
onPress={() => handlePress('Text + Icon')}
/>
</View>
<View style={styles.row}>
<Button
type="icon"
icon="settings"
iconComponent={Icon}
onPress={() => handlePress('Icon')}
/>
<Button
type="circle"
icon="add"
iconComponent={Icon}
onPress={() => handlePress('Circle')}
/>
<Button
type="square"
icon="menu"
iconComponent={Icon}
onPress={() => handlePress('Square')}
/>
</View>
{/* ICONS SECTION */}
<Text style={styles.sectionTitle}>With Icons</Text>
<View style={styles.row}>
<Button
title="Left Icon"
leftIcon="arrow-back"
iconComponent={Icon}
onPress={() => handlePress('Left Icon')}
/>
<Button
title="Right Icon"
rightIcon="arrow-forward"
iconComponent={Icon}
onPress={() => handlePress('Right Icon')}
/>
</View>
<View style={styles.row}>
<Button
title="Both Icons"
leftIcon="favorite"
rightIcon="send"
iconComponent={Icon}
onPress={() => handlePress('Both Icons')}
/>
</View>
{/* CIRCLE/SQUARE SIZES */}
<Text style={styles.sectionTitle}>Circle & Square Sizes</Text>
<View style={styles.row}>
<Button
type="circle"
size="small"
icon="add"
iconComponent={Icon}
onPress={() => handlePress('Small Circle')}
/>
<Button
type="circle"
size="medium"
icon="add"
iconComponent={Icon}
onPress={() => handlePress('Medium Circle')}
/>
<Button
type="circle"
size="large"
icon="add"
iconComponent={Icon}
onPress={() => handlePress('Large Circle')}
/>
</View>
<View style={styles.row}>
<Button
type="square"
size="small"
icon="menu"
iconComponent={Icon}
variant="outlined"
onPress={() => handlePress('Small Square')}
/>
<Button
type="square"
size="medium"
icon="menu"
iconComponent={Icon}
variant="outlined"
onPress={() => handlePress('Medium Square')}
/>
<Button
type="square"
size="large"
icon="menu"
iconComponent={Icon}
variant="outlined"
onPress={() => handlePress('Large Square')}
/>
</View>
{/* STATES SECTION */}
<Text style={styles.sectionTitle}>States</Text>
<View style={styles.row}>
<Button
title="Normal"
onPress={() => handlePress('Normal')}
/>
<Button
title="Disabled"
disabled={true}
onPress={() => handlePress('Disabled')}
/>
<Button
title={loading ? 'Loading...' : 'Click to Load'}
loading={loading}
onPress={handleLoadingPress}
/>
</View>
{/* DISABLED VARIANTS */}
<Text style={styles.sectionTitle}>Disabled Variants</Text>
<View style={styles.row}>
<Button title="Solid" variant="solid" disabled />
<Button title="Outlined" variant="outlined" disabled />
<Button title="Dashed" variant="dashed" disabled />
</View>
{/* CUSTOM COLORS SECTION */}
<Text style={styles.sectionTitle}>Custom Colors</Text>
<View style={styles.row}>
<Button
title="Blue"
color="#007AFF"
onPress={() => handlePress('Blue')}
/>
<Button
title="Green"
color="#34C759"
onPress={() => handlePress('Green')}
/>
<Button
title="Purple"
color="#AF52DE"
onPress={() => handlePress('Purple')}
/>
</View>
<View style={styles.row}>
<Button
title="Blue Outlined"
variant="outlined"
color="#007AFF"
onPress={() => handlePress('Blue Outlined')}
/>
<Button
title="Green Dashed"
variant="dashed"
color="#34C759"
onPress={() => handlePress('Green Dashed')}
/>
</View>
{/* CUSTOM STYLING SECTION */}
<Text style={styles.sectionTitle}>Custom Styling</Text>
<View style={styles.row}>
<Button
title="Rounded"
borderRadius={25}
onPress={() => handlePress('Rounded')}
/>
<Button
title="Shadow"
style={{
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 6,
elevation: 8,
}}
onPress={() => handlePress('Shadow')}
/>
</View>
<View style={styles.row}>
<Button
title="Custom Text"
textStyle={{
fontStyle: 'italic',
letterSpacing: 2,
textTransform: 'uppercase',
}}
onPress={() => handlePress('Custom Text')}
/>
</View>
{/* FULL WIDTH SECTION */}
<Text style={styles.sectionTitle}>Full Width</Text>
<Button
title="Full Width Button"
style={{ width: '100%' }}
onPress={() => handlePress('Full Width')}
/>
<View style={{ height: 10 }} />
<Button
title="Full Width Outlined"
variant="outlined"
leftIcon="check"
iconComponent={Icon}
style={{ width: '100%' }}
onPress={() => handlePress('Full Width Outlined')}
/>
{/* ACCESSIBILITY SECTION */}
<Text style={styles.sectionTitle}>Accessibility</Text>
<View style={styles.row}>
<Button
title="Accessible"
accessibilityLabel="Submit form button"
accessibilityHint="Double tap to submit the form"
onPress={() => handlePress('Accessible')}
/>
</View>
<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: 25,
marginBottom: 15,
color: '#333',
borderBottomWidth: 1,
borderBottomColor: '#ddd',
paddingBottom: 5,
},
row: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10,
marginBottom: 10,
},
});
export default ButtonTestScreen;Usage in Your App
// App.js or navigation screen
import ButtonTestScreen from './ButtonTestScreen';
// In your navigator or directly
<ButtonTestScreen />License
MIT © Atom Design
