UX Guidelines
- Always provide clear, descriptive labels for radio options
- Use RadioGroup for mutually exclusive choices
- Limit radio groups to 2-7 options for optimal usability
- Consider using dropdowns for more than 7 options
Radio provides a comprehensive solution for single-choice selection with built-in animations, group management, and multiple styling variants. It supports both controlled and uncontrolled usage with smooth spring animations and visual feedback.
import { RadioGroup, Radio, RadioIndicator, RadioIcon, RadioLabel} from 'rnc-theme';
const [selected, setSelected] = useState('option1');
<RadioGroup value={selected} onValueChange={setSelected}> <Radio value="option1"> <RadioLabel>Option 1</RadioLabel> </Radio> <Radio value="option2"> <RadioLabel>Option 2</RadioLabel> </Radio> <Radio value="option3"> <RadioLabel>Option 3</RadioLabel> </Radio></RadioGroup>
const [checked, setChecked] = useState(false);
<Radio value="single" checked={checked} onCheckedChange={setChecked}> <RadioLabel>Single Radio Option</RadioLabel></Radio>
<RadioGroup value={value} onValueChange={setValue}> <Radio value="premium"> <RadioIndicator> <RadioIcon icon={<StarIcon />} /> </RadioIndicator> <RadioLabel>Premium Plan</RadioLabel> </Radio></RadioGroup>
<RadioGroup value={value} onValueChange={setValue} disabled> <Radio value="disabled1"> <RadioLabel>Disabled Option 1</RadioLabel> </Radio> <Radio value="disabled2"> <RadioLabel>Disabled Option 2</RadioLabel> </Radio></RadioGroup>
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | - | Radio components to render |
value | string | '' | Currently selected radio value |
onValueChange | (value: string) => void | - | Callback when selection changes |
disabled | boolean | false | Disable entire radio group |
style | StyleProp<ViewStyle> | - | Additional container styles |
Prop | Type | Default | Description |
---|---|---|---|
value | string | - | Required. Unique identifier for radio |
checked | boolean | - | Controlled checked state (individual use) |
onCheckedChange | (checked: boolean) => void | - | Callback for individual radio changes |
size | ComponentSize | 'md' | Radio size (xs, sm, md, lg, xl) |
variant | ComponentVariant | 'default' | Visual style variant |
disabled | boolean | false | Disable this specific radio |
children | React.ReactNode | - | RadioLabel and other content |
style | StyleProp<ViewStyle> | - | Additional radio styles |
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | - | Custom indicator content |
size | ComponentSize | 'md' | Indicator size |
variant | ComponentVariant | 'default' | Indicator variant |
style | StyleProp<ViewStyle> | - | Additional indicator styles |
Prop | Type | Default | Description |
---|---|---|---|
icon | React.ReactNode | - | Custom icon component |
size | ComponentSize | 'md' | Icon size |
variant | ComponentVariant | 'default' | Icon variant |
style | StyleProp<ViewStyle> | - | Additional icon styles |
Prop | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | - | Required. Label text content |
size | ComponentSize | 'md' | Label text size |
style | TextStyle | - | Additional label styles |
Variant | Description | Use Case |
---|---|---|
default | Standard radio appearance | General form inputs |
primary | Primary brand color | Important selections |
secondary | Secondary brand color | Alternative selections |
outline | Outlined style | Minimal designs |
filled | Filled background | Prominent selections |
ghost | Subtle appearance | Low-emphasis choices |
success | Success state color | Confirmation choices |
error | Error state color | Invalid selections |
warning | Warning state color | Caution choices |
info | Information color | Info selections |
destructive | Destructive action color | Deletion confirmations |
const SettingsForm = () => { const [theme, setTheme] = useState('light'); const [notifications, setNotifications] = useState('all'); const [language, setLanguage] = useState('en');
return ( <VStack spacing="xl" padding="lg"> {/* Theme Selection */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Theme</Text> <RadioGroup value={theme} onValueChange={setTheme}> <Radio value="light" variant="primary"> <RadioLabel>Light Mode</RadioLabel> </Radio> <Radio value="dark" variant="primary"> <RadioLabel>Dark Mode</RadioLabel> </Radio> <Radio value="auto" variant="primary"> <RadioLabel>System Default</RadioLabel> </Radio> </RadioGroup> </VStack>
{/* Notification Settings */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Notifications</Text> <RadioGroup value={notifications} onValueChange={setNotifications}> <Radio value="all" variant="success"> <RadioLabel>All Notifications</RadioLabel> </Radio> <Radio value="important" variant="warning"> <RadioLabel>Important Only</RadioLabel> </Radio> <Radio value="none" variant="error"> <RadioLabel>None</RadioLabel> </Radio> </RadioGroup> </VStack>
{/* Language Selection */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Language</Text> <RadioGroup value={language} onValueChange={setLanguage}> <Radio value="en" size="sm"> <RadioLabel>English</RadioLabel> </Radio> <Radio value="id" size="sm"> <RadioLabel>Bahasa Indonesia</RadioLabel> </Radio> <Radio value="es" size="sm"> <RadioLabel>Español</RadioLabel> </Radio> </RadioGroup> </VStack> </VStack> );};
<VStack spacing="lg" padding="lg"> <Text style={{ fontSize: 18, fontWeight: 'bold' }}>Radio Sizes</Text>
<VStack spacing="md"> <Radio value="xs" size="xs" checked> <RadioLabel size="xs">Extra Small Radio</RadioLabel> </Radio>
<Radio value="sm" size="sm" checked> <RadioLabel size="sm">Small Radio</RadioLabel> </Radio>
<Radio value="md" size="md" checked> <RadioLabel size="md">Medium Radio (Default)</RadioLabel> </Radio>
<Radio value="lg" size="lg" checked> <RadioLabel size="lg">Large Radio</RadioLabel> </Radio>
<Radio value="xl" size="xl" checked> <RadioLabel size="xl">Extra Large Radio</RadioLabel> </Radio> </VStack></VStack>
const SubscriptionPlans = () => { const [selectedPlan, setSelectedPlan] = useState('basic');
const plans = [ { id: 'basic', name: 'Basic Plan', price: '$9.99/month', features: ['10 Projects', '5GB Storage', 'Email Support'], variant: 'outline' }, { id: 'pro', name: 'Pro Plan', price: '$19.99/month', features: ['Unlimited Projects', '100GB Storage', 'Priority Support'], variant: 'primary' }, { id: 'enterprise', name: 'Enterprise Plan', price: '$49.99/month', features: ['Everything in Pro', 'Custom Integrations', '24/7 Phone Support'], variant: 'success' } ];
return ( <VStack spacing="lg" padding="lg"> <Text style={{ fontSize: 20, fontWeight: 'bold', textAlign: 'center' }}> Choose Your Plan </Text>
<RadioGroup value={selectedPlan} onValueChange={setSelectedPlan}> {plans.map(plan => ( <Box key={plan.id} variant="card" padding="lg" style={{ borderWidth: selectedPlan === plan.id ? 2 : 1, borderColor: selectedPlan === plan.id ? '#007AFF' : '#E0E0E0' }} > <Radio value={plan.id} variant={plan.variant}> <VStack spacing="sm" flex={1}> <HStack justify="space-between" align="center"> <RadioLabel style={{ fontSize: 18, fontWeight: '600' }}> {plan.name} </RadioLabel> <Text style={{ fontSize: 16, fontWeight: 'bold', color: '#007AFF' }}> {plan.price} </Text> </HStack>
<VStack spacing="xs"> {plan.features.map((feature, index) => ( <Text key={index} style={{ fontSize: 14, color: '#666' }}> • {feature} </Text> ))} </VStack> </VStack> </Radio> </Box> ))} </RadioGroup>
<Button title={`Subscribe to ${plans.find(p => p.id === selectedPlan)?.name}`} variant="primary" disabled={!selectedPlan} /> </VStack> );};
const PaymentMethods = () => { const [paymentMethod, setPaymentMethod] = useState('');
return ( <VStack spacing="lg" padding="lg"> <Text style={{ fontSize: 18, fontWeight: '600' }}>Payment Method</Text>
<RadioGroup value={paymentMethod} onValueChange={setPaymentMethod}> <Radio value="card" variant="primary"> <RadioIndicator> <RadioIcon icon={<CreditCardIcon />} /> </RadioIndicator> <VStack spacing="xs" flex={1}> <RadioLabel style={{ fontWeight: '600' }}>Credit Card</RadioLabel> <Text style={{ fontSize: 12, color: '#666' }}> Pay with Visa, Mastercard, or American Express </Text> </VStack> </Radio>
<Radio value="paypal" variant="secondary"> <RadioIndicator> <RadioIcon icon={<PaypalIcon />} /> </RadioIndicator> <VStack spacing="xs" flex={1}> <RadioLabel style={{ fontWeight: '600' }}>PayPal</RadioLabel> <Text style={{ fontSize: 12, color: '#666' }}> Pay securely with your PayPal account </Text> </VStack> </Radio>
<Radio value="apple" variant="ghost"> <RadioIndicator> <RadioIcon icon={<AppleIcon />} /> </RadioIndicator> <VStack spacing="xs" flex={1}> <RadioLabel style={{ fontWeight: '600' }}>Apple Pay</RadioLabel> <Text style={{ fontSize: 12, color: '#666' }}> Pay with Touch ID or Face ID </Text> </VStack> </Radio>
<Radio value="google" variant="ghost"> <RadioIndicator> <RadioIcon icon={<GoogleIcon />} /> </RadioIndicator> <VStack spacing="xs" flex={1}> <RadioLabel style={{ fontWeight: '600' }}>Google Pay</RadioLabel> <Text style={{ fontSize: 12, color: '#666' }}> Pay with your Google account </Text> </VStack> </Radio> </RadioGroup> </VStack> );};
const SurveyForm = () => { const [satisfaction, setSatisfaction] = useState(''); const [frequency, setFrequency] = useState(''); const [recommendation, setRecommendation] = useState('');
return ( <VStack spacing="xl" padding="lg"> <Text style={{ fontSize: 20, fontWeight: 'bold' }}>Customer Survey</Text>
{/* Satisfaction Rating */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}> How satisfied are you with our service? </Text> <RadioGroup value={satisfaction} onValueChange={setSatisfaction}> <Radio value="very-satisfied" variant="success" size="lg"> <RadioLabel>😍 Very Satisfied</RadioLabel> </Radio> <Radio value="satisfied" variant="primary" size="lg"> <RadioLabel>😊 Satisfied</RadioLabel> </Radio> <Radio value="neutral" variant="outline" size="lg"> <RadioLabel>😐 Neutral</RadioLabel> </Radio> <Radio value="dissatisfied" variant="warning" size="lg"> <RadioLabel>😞 Dissatisfied</RadioLabel> </Radio> <Radio value="very-dissatisfied" variant="error" size="lg"> <RadioLabel>😡 Very Dissatisfied</RadioLabel> </Radio> </RadioGroup> </VStack>
{/* Usage Frequency */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}> How often do you use our product? </Text> <RadioGroup value={frequency} onValueChange={setFrequency}> <Radio value="daily"> <RadioLabel>Daily</RadioLabel> </Radio> <Radio value="weekly"> <RadioLabel>Weekly</RadioLabel> </Radio> <Radio value="monthly"> <RadioLabel>Monthly</RadioLabel> </Radio> <Radio value="rarely"> <RadioLabel>Rarely</RadioLabel> </Radio> </RadioGroup> </VStack>
{/* Recommendation */} <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}> Would you recommend us to others? </Text> <RadioGroup value={recommendation} onValueChange={setRecommendation}> <Radio value="definitely" variant="success"> <RadioLabel>Definitely</RadioLabel> </Radio> <Radio value="probably" variant="primary"> <RadioLabel>Probably</RadioLabel> </Radio> <Radio value="maybe" variant="outline"> <RadioLabel>Maybe</RadioLabel> </Radio> <Radio value="probably-not" variant="warning"> <RadioLabel>Probably Not</RadioLabel> </Radio> <Radio value="definitely-not" variant="error"> <RadioLabel>Definitely Not</RadioLabel> </Radio> </RadioGroup> </VStack>
<Button title="Submit Survey" variant="primary" disabled={!satisfaction || !frequency || !recommendation} /> </VStack> );};
const DynamicRadioForm = () => { const [category, setCategory] = useState(''); const [subcategory, setSubcategory] = useState('');
const categories = { electronics: ['Smartphones', 'Laptops', 'Tablets', 'Accessories'], clothing: ['Men', 'Women', 'Kids', 'Shoes'], books: ['Fiction', 'Non-Fiction', 'Educational', 'Comics'] };
const subcategories = categories[category] || [];
return ( <VStack spacing="lg" padding="lg"> <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Category</Text> <RadioGroup value={category} onValueChange={(value) => { setCategory(value); setSubcategory(''); // Reset subcategory }}> <Radio value="electronics"> <RadioLabel>Electronics</RadioLabel> </Radio> <Radio value="clothing"> <RadioLabel>Clothing</RadioLabel> </Radio> <Radio value="books"> <RadioLabel>Books</RadioLabel> </Radio> </RadioGroup> </VStack>
{category && ( <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Subcategory</Text> <RadioGroup value={subcategory} onValueChange={setSubcategory}> {subcategories.map(sub => ( <Radio key={sub} value={sub.toLowerCase()}> <RadioLabel>{sub}</RadioLabel> </Radio> ))} </RadioGroup> </VStack> )} </VStack> );};
const ConditionalForm = () => { const [userType, setUserType] = useState(''); const [experience, setExperience] = useState(''); const [companySize, setCompanySize] = useState('');
return ( <VStack spacing="lg" padding="lg"> <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>I am a...</Text> <RadioGroup value={userType} onValueChange={setUserType}> <Radio value="individual"> <RadioLabel>Individual Developer</RadioLabel> </Radio> <Radio value="business"> <RadioLabel>Business User</RadioLabel> </Radio> <Radio value="student"> <RadioLabel>Student</RadioLabel> </Radio> </RadioGroup> </VStack>
{userType === 'individual' && ( <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Experience Level</Text> <RadioGroup value={experience} onValueChange={setExperience}> <Radio value="beginner" variant="info"> <RadioLabel>Beginner (0-1 years)</RadioLabel> </Radio> <Radio value="intermediate" variant="primary"> <RadioLabel>Intermediate (2-5 years)</RadioLabel> </Radio> <Radio value="expert" variant="success"> <RadioLabel>Expert (5+ years)</RadioLabel> </Radio> </RadioGroup> </VStack> )}
{userType === 'business' && ( <VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}>Company Size</Text> <RadioGroup value={companySize} onValueChange={setCompanySize}> <Radio value="startup" variant="outline"> <RadioLabel>Startup (1-10 employees)</RadioLabel> </Radio> <Radio value="small" variant="primary"> <RadioLabel>Small Business (11-50 employees)</RadioLabel> </Radio> <Radio value="medium" variant="secondary"> <RadioLabel>Medium Business (51-200 employees)</RadioLabel> </Radio> <Radio value="enterprise" variant="success"> <RadioLabel>Enterprise (200+ employees)</RadioLabel> </Radio> </RadioGroup> </VStack> )} </VStack> );};
const ValidatedRadioForm = () => { const [priority, setPriority] = useState(''); const [urgency, setUrgency] = useState(''); const [submitted, setSubmitted] = useState(false);
const handleSubmit = () => { setSubmitted(true); if (priority && urgency) { // Process form console.log('Form submitted:', { priority, urgency }); } };
const priorityError = submitted && !priority; const urgencyError = submitted && !urgency;
return ( <VStack spacing="lg" padding="lg"> <Text style={{ fontSize: 18, fontWeight: 'bold' }}>Support Ticket</Text>
<VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}> Priority Level * </Text> <RadioGroup value={priority} onValueChange={setPriority}> <Radio value="low" variant={priorityError ? 'error' : 'success'}> <RadioLabel>Low Priority</RadioLabel> </Radio> <Radio value="medium" variant={priorityError ? 'error' : 'warning'}> <RadioLabel>Medium Priority</RadioLabel> </Radio> <Radio value="high" variant={priorityError ? 'error' : 'error'}> <RadioLabel>High Priority</RadioLabel> </Radio> </RadioGroup> {priorityError && ( <Text style={{ color: 'red', fontSize: 12 }}> Please select a priority level </Text> )} </VStack>
<VStack spacing="md"> <Text style={{ fontSize: 16, fontWeight: '600' }}> Urgency * </Text> <RadioGroup value={urgency} onValueChange={setUrgency}> <Radio value="can-wait" variant={urgencyError ? 'error' : 'outline'}> <RadioLabel>Can Wait</RadioLabel> </Radio> <Radio value="soon" variant={urgencyError ? 'error' : 'primary'}> <RadioLabel>Needed Soon</RadioLabel> </Radio> <Radio value="immediate" variant={urgencyError ? 'error' : 'destructive'}> <RadioLabel>Immediate Attention</RadioLabel> </Radio> </RadioGroup> {urgencyError && ( <Text style={{ color: 'red', fontSize: 12 }}> Please select urgency level </Text> )} </VStack>
<Button title="Submit Ticket" onPress={handleSubmit} variant="primary" /> </VStack> );};
The Radio component uses smooth spring animations that can be customized through the theme configuration:
// Default animation configurationconst DEFAULT_SPRING_CONFIG = { damping: 15, stiffness: 200, mass: 0.5};
const BORDER_TIMING_CONFIG = { duration: 200};
UX Guidelines
Visual Design
Performance