Custom Styling with Borders
Create unique designs by customizing track and thumb borders.- Bordered Switch
- Outlined Style
- Thick Border
import { useState } from 'react';
import { Switch } from 'react-native-switchery';
export default function BorderedSwitch() {
const [isEnabled, setIsEnabled] = useState(false);
return (
<Switch
value={isEnabled}
onValueChange={setIsEnabled}
variant="primary"
trackBorderColor="#2196F3"
trackBorderWidth={2}
thumbBorderColor="#1976D2"
thumbBorderWidth={2}
/>
);
}
import { useState } from 'react';
import { Switch } from 'react-native-switchery';
export default function OutlinedSwitch() {
const [isEnabled, setIsEnabled] = useState(false);
return (
<Switch
value={isEnabled}
onValueChange={setIsEnabled}
activeColor="transparent"
inactiveColor="transparent"
thumbColor="#6366F1"
trackBorderColor="#6366F1"
trackBorderWidth={3}
thumbBorderColor="#6366F1"
thumbBorderWidth={1}
/>
);
}
import { useState } from 'react';
import { Switch } from 'react-native-switchery';
export default function ThickBorderSwitch() {
const [isEnabled, setIsEnabled] = useState(false);
return (
<Switch
value={isEnabled}
onValueChange={setIsEnabled}
activeColor="#10B981"
inactiveColor="#E5E7EB"
thumbColor="#FFFFFF"
trackBorderColor={isEnabled ? "#059669" : "#9CA3AF"}
trackBorderWidth={4}
/>
);
}
Form Integration
Integrate switches seamlessly into form components with labels and validation.import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Switch } from 'react-native-switchery';
type FormData = {
notifications: boolean;
emailAlerts: boolean;
darkMode: boolean;
dataSharing: boolean;
};
export default function SettingsForm() {
const [formData, setFormData] = useState<FormData>({
notifications: true,
emailAlerts: false,
darkMode: false,
dataSharing: false,
});
const updateSetting = (key: keyof FormData, value: boolean) => {
setFormData(prev => ({ ...prev, [key]: value }));
};
return (
<View style={styles.form}>
<View style={styles.formRow}>
<View style={styles.labelContainer}>
<Text style={styles.label}>Push Notifications</Text>
<Text style={styles.description}>
Receive notifications about updates
</Text>
</View>
<Switch
value={formData.notifications}
onValueChange={(value) => updateSetting('notifications', value)}
variant="primary"
accessibilityLabel="Enable push notifications"
/>
</View>
<View style={styles.formRow}>
<View style={styles.labelContainer}>
<Text style={styles.label}>Email Alerts</Text>
<Text style={styles.description}>
Get important updates via email
</Text>
</View>
<Switch
value={formData.emailAlerts}
onValueChange={(value) => updateSetting('emailAlerts', value)}
variant="info"
accessibilityLabel="Enable email alerts"
/>
</View>
<View style={styles.formRow}>
<View style={styles.labelContainer}>
<Text style={styles.label}>Dark Mode</Text>
<Text style={styles.description}>
Switch to dark theme
</Text>
</View>
<Switch
value={formData.darkMode}
onValueChange={(value) => updateSetting('darkMode', value)}
variant="success"
accessibilityLabel="Enable dark mode"
/>
</View>
<View style={styles.formRow}>
<View style={styles.labelContainer}>
<Text style={styles.label}>Data Sharing</Text>
<Text style={styles.description}>
Share usage data for analytics
</Text>
</View>
<Switch
value={formData.dataSharing}
onValueChange={(value) => updateSetting('dataSharing', value)}
variant="warning"
accessibilityLabel="Enable data sharing"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
form: {
padding: 16,
gap: 20,
},
formRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 8,
},
labelContainer: {
flex: 1,
marginRight: 16,
},
label: {
fontSize: 16,
fontWeight: '600',
color: '#111827',
marginBottom: 4,
},
description: {
fontSize: 14,
color: '#6B7280',
},
});
Use meaningful
accessibilityLabel props to ensure your switches are accessible to all users.Dynamic Styling Based on State
Adjust switch appearance dynamically based on its current state or external conditions.import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Switch } from 'react-native-switchery';
export default function DynamicSwitch() {
const [isPremium, setIsPremium] = useState(false);
const [isLowBattery, setIsLowBattery] = useState(true);
// Dynamic colors based on state
const premiumColors = isPremium
? { active: '#FFD700', inactive: '#FFA500', thumb: '#FFFFFF' }
: { active: '#10B981', inactive: '#E5E7EB', thumb: '#FFFFFF' };
return (
<View style={styles.container}>
{/* Premium membership with dynamic colors */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Premium Membership</Text>
<Switch
value={isPremium}
onValueChange={setIsPremium}
activeColor={premiumColors.active}
inactiveColor={premiumColors.inactive}
thumbColor={premiumColors.thumb}
size="large"
/>
<Text style={styles.status}>
{isPremium ? 'Premium Active' : 'Free Plan'}
</Text>
</View>
{/* Battery saver mode with conditional rendering */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Battery Saver</Text>
<Switch
value={isLowBattery}
onValueChange={setIsLowBattery}
variant={isLowBattery ? 'warning' : 'success'}
size="default"
/>
<Text style={[
styles.status,
{ color: isLowBattery ? '#F59E0B' : '#10B981' }
]}>
{isLowBattery ? 'Low Battery Mode' : 'Normal Mode'}
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
gap: 24,
},
section: {
alignItems: 'center',
gap: 12,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
color: '#111827',
},
status: {
fontSize: 14,
color: '#6B7280',
},
});
Multiple Switches with Grid Layout
Manage multiple related settings with a clean, organized layout.import { useState } from 'react';
import { View, Text, StyleSheet, ScrollView } from 'react-native';
import { Switch } from 'react-native-switchery';
type Permission = {
id: string;
label: string;
description: string;
enabled: boolean;
variant: 'primary' | 'danger' | 'warning' | 'success' | 'info';
};
export default function PermissionsGrid() {
const [permissions, setPermissions] = useState<Permission[]>([
{
id: 'camera',
label: 'Camera',
description: 'Access device camera',
enabled: true,
variant: 'primary',
},
{
id: 'location',
label: 'Location',
description: 'Access GPS location',
enabled: false,
variant: 'info',
},
{
id: 'microphone',
label: 'Microphone',
description: 'Record audio',
enabled: true,
variant: 'success',
},
{
id: 'contacts',
label: 'Contacts',
description: 'Access contact list',
enabled: false,
variant: 'warning',
},
{
id: 'storage',
label: 'Storage',
description: 'Read/write files',
enabled: true,
variant: 'primary',
},
{
id: 'notifications',
label: 'Notifications',
description: 'Send push notifications',
enabled: false,
variant: 'danger',
},
]);
const togglePermission = (id: string) => {
setPermissions(prev =>
prev.map(perm =>
perm.id === id ? { ...perm, enabled: !perm.enabled } : perm
)
);
};
return (
<ScrollView style={styles.container}>
<View style={styles.grid}>
{permissions.map(permission => (
<View key={permission.id} style={styles.card}>
<View style={styles.cardHeader}>
<Text style={styles.cardTitle}>{permission.label}</Text>
<Switch
value={permission.enabled}
onValueChange={() => togglePermission(permission.id)}
variant={permission.variant}
size="small"
testID={`switch-${permission.id}`}
/>
</View>
<Text style={styles.cardDescription}>
{permission.description}
</Text>
<Text style={[
styles.statusBadge,
permission.enabled && styles.statusBadgeActive
]}>
{permission.enabled ? 'Granted' : 'Denied'}
</Text>
</View>
))}
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F9FAFB',
},
grid: {
padding: 16,
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
card: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
padding: 16,
width: '48%',
minWidth: 150,
borderWidth: 1,
borderColor: '#E5E7EB',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 1,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
cardTitle: {
fontSize: 16,
fontWeight: '600',
color: '#111827',
},
cardDescription: {
fontSize: 13,
color: '#6B7280',
marginBottom: 8,
},
statusBadge: {
fontSize: 11,
fontWeight: '500',
color: '#EF4444',
textTransform: 'uppercase',
},
statusBadgeActive: {
color: '#10B981',
},
});
Controlled with External State
Synchronize switch state with external data sources or complex state management.import { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { Switch } from 'react-native-switchery';
export default function ControlledSwitch() {
const [isLoading, setIsLoading] = useState(false);
const [autoSave, setAutoSave] = useState(false);
const [lastSaved, setLastSaved] = useState<Date | null>(null);
// Simulate API call when switch changes
const handleToggle = async (value: boolean) => {
setIsLoading(true);
try {
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Update state only after successful "save"
setAutoSave(value);
setLastSaved(new Date());
} catch (error) {
// Handle error - switch reverts to previous state
console.error('Failed to save setting:', error);
} finally {
setIsLoading(false);
}
};
return (
<View style={styles.container}>
<View style={styles.header}>
<View style={styles.labelContainer}>
<Text style={styles.label}>Auto-Save</Text>
<Text style={styles.description}>
Automatically save changes
</Text>
</View>
<View style={styles.switchContainer}>
{isLoading && (
<ActivityIndicator
size="small"
color="#2196F3"
style={styles.loader}
/>
)}
<Switch
value={autoSave}
onValueChange={handleToggle}
disabled={isLoading}
variant="success"
size="default"
/>
</View>
</View>
{lastSaved && (
<Text style={styles.timestamp}>
Last saved: {lastSaved.toLocaleTimeString()}
</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
backgroundColor: '#FFFFFF',
borderRadius: 12,
borderWidth: 1,
borderColor: '#E5E7EB',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
labelContainer: {
flex: 1,
},
label: {
fontSize: 16,
fontWeight: '600',
color: '#111827',
marginBottom: 4,
},
description: {
fontSize: 14,
color: '#6B7280',
},
switchContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
loader: {
marginRight: 8,
},
timestamp: {
marginTop: 12,
fontSize: 12,
color: '#9CA3AF',
fontStyle: 'italic',
},
});
Always handle asynchronous operations gracefully by showing loading states and keeping the switch disabled during updates.
Testing with TestID
Implement switches with proper test identifiers for automated testing.import { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { Switch } from 'react-native-switchery';
export default function TestableSwitch() {
const [settings, setSettings] = useState({
feature1: false,
feature2: true,
feature3: false,
});
return (
<View style={styles.container}>
<Switch
value={settings.feature1}
onValueChange={(value) =>
setSettings(prev => ({ ...prev, feature1: value }))
}
testID="switch-feature-1"
accessibilityLabel="Enable Feature 1"
accessibilityHint="Toggles the first feature on or off"
/>
<Switch
value={settings.feature2}
onValueChange={(value) =>
setSettings(prev => ({ ...prev, feature2: value }))
}
testID="switch-feature-2"
accessibilityLabel="Enable Feature 2"
accessibilityHint="Toggles the second feature on or off"
/>
<Switch
value={settings.feature3}
onValueChange={(value) =>
setSettings(prev => ({ ...prev, feature3: value }))
}
disabled={true}
testID="switch-feature-3"
accessibilityLabel="Enable Feature 3"
accessibilityHint="This feature is currently unavailable"
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
gap: 16,
},
});
Testing Example
import { render, fireEvent } from '@testing-library/react-native';
import TestableSwitch from './TestableSwitch';
describe('TestableSwitch', () => {
it('should toggle switch when pressed', () => {
const { getByTestId } = render(<TestableSwitch />);
const switch1 = getByTestId('switch-feature-1');
fireEvent.press(switch1);
expect(switch1.props.accessibilityState.checked).toBe(true);
});
it('should not toggle disabled switch', () => {
const { getByTestId } = render(<TestableSwitch />);
const switch3 = getByTestId('switch-feature-3');
expect(switch3.props.accessibilityState.disabled).toBe(true);
fireEvent.press(switch3);
expect(switch3.props.accessibilityState.checked).toBe(false);
});
});
Combine
testID with accessibility props to make your components both testable and accessible.