Skip to content

Input

Input provides a versatile text input solution with built-in animations, validation states, floating labels, and specialized input types including password, search, and textarea variants. Features smooth transitions, customizable styling, and comprehensive accessibility support.

import { Input } from 'rnc-theme';
<Input
label="Email"
placeholder="Enter your email"
value={email}
onChangeText={setEmail}
/>
PropTypeDefaultDescription
labelstring-Input field label
placeholderstring-Placeholder text
valuestring-Controlled input value
defaultValuestring-Default uncontrolled value
onChangeText(text: string) => void-Text change handler
variantComponentVariant'default'Visual style variant
sizeComponentSize'md'Input size (sm, md, lg)
stateComponentState'default'Validation state
PropTypeDefaultDescription
borderRadiuskeyof Theme['components']['borderRadius']'md'Border radius value
inputStyleTextStyle-Custom input text styles
labelStyleTextStyle-Custom label styles
helperTextStyleTextStyle-Custom helper text styles
styleViewStyle-Container style overrides
PropTypeDefaultDescription
disabledbooleanfalseDisable input interactions
readOnlybooleanfalseMake input read-only
requiredbooleanfalseMark field as required
animatedbooleantrueEnable animations
autoFocusbooleanfalseAuto-focus on mount
floatingLabelbooleanfalseUse floating label style
PropTypeDefaultDescription
errorstring-Error message to display
helperTextstring-Helper text below input
maxLengthnumber-Maximum character limit
showCharacterCountbooleanfalseShow character counter
PropTypeDefaultDescription
leftIconReact.ReactNode-Icon on the left side
rightIconReact.ReactNode-Icon on the right side
onRightIconPress() => void-Right icon press handler
PropTypeDefaultDescription
isPasswordInputbooleanfalseEnable password input mode
isSearchInputbooleanfalseEnable search input mode
isTextAreaInputbooleanfalseEnable textarea mode
showPasswordIconReact.ReactNode-Custom show password icon
hidePasswordIconReact.ReactNode-Custom hide password icon
PropTypeDefaultDescription
multilinebooleanfalseEnable multiline input
numberOfLinesnumber1Number of visible lines
VariantDescriptionUse Case
defaultStandard input styleGeneral text inputs
primaryPrimary brand stylingImportant form fields
secondarySecondary stylingSupporting form fields
outlineOutlined border styleProminent form inputs
filledFilled background styleDense form layouts
ghostMinimal stylingSubtle input fields
successSuccess state stylingValidated fields
errorError state stylingInvalid fields
warningWarning state stylingFields needing attention
infoInformation stylingInformational inputs
destructiveDestructive action stylingDangerous operations
StateDescriptionVisual Effect
defaultNormal input stateStandard appearance
focusFocused input stateEnhanced border, colors
activeActive interactionPressed appearance
disabledDisabled inputReduced opacity
loadingLoading stateLoading indicators
errorError validationRed border, error text
successSuccess validationGreen border
warningWarning stateOrange border
const BasicForm = () => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
return (
<VStack spacing="lg" padding="lg">
<Input
label="First Name"
placeholder="Enter your first name"
value={firstName}
onChangeText={setFirstName}
required
/>
<Input
label="Last Name"
placeholder="Enter your last name"
value={lastName}
onChangeText={setLastName}
required
/>
<Input
label="Email Address"
placeholder="Enter your email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
required
state={emailError ? 'error' : 'default'}
error={emailError}
/>
</VStack>
);
};
const SpecializedInputs = () => {
const [password, setPassword] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [description, setDescription] = useState('');
return (
<VStack spacing="lg" padding="lg">
{/* Password Input */}
<Input
label="Password"
placeholder="Enter your password"
value={password}
onChangeText={setPassword}
isPasswordInput
required
helperText="Must be at least 8 characters"
/>
{/* Search Input */}
<Input
placeholder="Search products..."
value={searchQuery}
onChangeText={setSearchQuery}
isSearchInput
variant="outline"
/>
{/* Textarea Input */}
<Input
label="Description"
placeholder="Enter a detailed description..."
value={description}
onChangeText={setDescription}
isTextAreaInput
numberOfLines={4}
maxLength={500}
showCharacterCount
/>
</VStack>
);
};
const FloatingLabelForm = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
company: ''
});
const updateField = (field: string) => (value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
return (
<VStack spacing="xl" padding="lg">
<Input
label="Full Name"
value={formData.name}
onChangeText={updateField('name')}
floatingLabel
variant="outline"
required
/>
<Input
label="Email Address"
value={formData.email}
onChangeText={updateField('email')}
floatingLabel
variant="outline"
keyboardType="email-address"
required
/>
<Input
label="Phone Number"
value={formData.phone}
onChangeText={updateField('phone')}
floatingLabel
variant="outline"
keyboardType="phone-pad"
/>
<Input
label="Company"
value={formData.company}
onChangeText={updateField('company')}
floatingLabel
variant="outline"
/>
</VStack>
);
};
const ValidationExample = () => {
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const validateUsername = (value: string) => {
if (value.length < 3) return 'Username must be at least 3 characters';
if (!/^[a-zA-Z0-9_]+$/.test(value)) return 'Only letters, numbers, and underscores allowed';
return '';
};
const validateEmail = (value: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value) ? '' : 'Please enter a valid email address';
};
const usernameError = validateUsername(username);
const emailError = validateEmail(email);
return (
<VStack spacing="lg" padding="lg">
<Input
label="Username"
placeholder="Choose a username"
value={username}
onChangeText={setUsername}
state={usernameError ? 'error' : username.length > 0 ? 'success' : 'default'}
error={usernameError}
helperText="3+ characters, letters, numbers, and underscores only"
required
/>
<Input
label="Email"
placeholder="Enter your email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
state={emailError ? 'error' : email.length > 0 ? 'success' : 'default'}
error={emailError}
required
/>
<Input
label="Confirm Password"
placeholder="Confirm your password"
value={confirmPassword}
onChangeText={setConfirmPassword}
isPasswordInput
state={confirmPassword !== password && confirmPassword.length > 0 ? 'error' : 'default'}
error={confirmPassword !== password && confirmPassword.length > 0 ? 'Passwords do not match' : ''}
required
/>
</VStack>
);
};
const IconInputs = () => {
const [location, setLocation] = useState('');
const [budget, setBudget] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const handleLocationDetect = () => {
// Implement location detection
console.log('Detecting location...');
};
const handleClearSearch = () => {
setSearchTerm('');
};
return (
<VStack spacing="lg" padding="lg">
<Input
label="Location"
placeholder="Enter your location"
value={location}
onChangeText={setLocation}
leftIcon={<MapPinIcon size={20} color="#666" />}
rightIcon={<GpsIcon size={20} color="#0066CC" />}
onRightIconPress={handleLocationDetect}
/>
<Input
label="Budget Range"
placeholder="Enter maximum budget"
value={budget}
onChangeText={setBudget}
leftIcon={<DollarSignIcon size={20} color="#666" />}
keyboardType="numeric"
/>
<Input
placeholder="Search anything..."
value={searchTerm}
onChangeText={setSearchTerm}
leftIcon={<SearchIcon size={20} color="#666" />}
rightIcon={
searchTerm.length > 0 ? (
<XIcon size={20} color="#666" />
) : (
<MicIcon size={20} color="#666" />
)
}
onRightIconPress={searchTerm.length > 0 ? handleClearSearch : handleVoiceSearch}
variant="filled"
/>
</VStack>
);
};
<VStack spacing="lg" align="stretch">
<Input
size="sm"
label="Small Input"
placeholder="Small size input"
variant="outline"
/>
<Input
size="md"
label="Medium Input"
placeholder="Medium size input (default)"
variant="outline"
/>
<Input
size="lg"
label="Large Input"
placeholder="Large size input"
variant="outline"
/>
</VStack>
const ContactForm = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
subject: '',
message: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const updateField = (field: string) => (value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
// Clear error when user starts typing
if (errors[field]) {
setErrors(prev => ({ ...prev, [field]: '' }));
}
};
const validateForm = () => {
const newErrors = {};
if (!formData.name.trim()) newErrors.name = 'Name is required';
if (!formData.email.trim()) newErrors.email = 'Email is required';
if (!formData.message.trim()) newErrors.message = 'Message is required';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async () => {
if (!validateForm()) return;
setIsSubmitting(true);
try {
await submitContactForm(formData);
// Reset form or show success
} finally {
setIsSubmitting(false);
}
};
return (
<VStack spacing="lg" padding="lg">
<Text style={styles.formTitle}>Contact Us</Text>
<Input
label="Full Name"
placeholder="Enter your full name"
value={formData.name}
onChangeText={updateField('name')}
required
error={errors.name}
leftIcon={<UserIcon size={20} color="#666" />}
variant="outline"
/>
<Input
label="Email Address"
placeholder="Enter your email"
value={formData.email}
onChangeText={updateField('email')}
keyboardType="email-address"
autoCapitalize="none"
required
error={errors.email}
leftIcon={<MailIcon size={20} color="#666" />}
variant="outline"
/>
<Input
label="Phone Number"
placeholder="Enter your phone number"
value={formData.phone}
onChangeText={updateField('phone')}
keyboardType="phone-pad"
leftIcon={<PhoneIcon size={20} color="#666" />}
variant="outline"
/>
<Input
label="Subject"
placeholder="What is this regarding?"
value={formData.subject}
onChangeText={updateField('subject')}
variant="outline"
/>
<Input
label="Message"
placeholder="Enter your message here..."
value={formData.message}
onChangeText={updateField('message')}
isTextAreaInput
numberOfLines={5}
maxLength={1000}
showCharacterCount
required
error={errors.message}
variant="outline"
/>
<Button
onPress={handleSubmit}
disabled={isSubmitting}
loading={isSubmitting}
variant="primary"
size="lg"
fullWidth
>
<ButtonText>Send Message</ButtonText>
</Button>
</VStack>
);
};
const useFormValidation = (rules) => {
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const validate = (field, value) => {
const rule = rules[field];
if (!rule) return '';
if (rule.required && !value.trim()) {
return rule.requiredMessage || `${field} is required`;
}
if (rule.minLength && value.length < rule.minLength) {
return `${field} must be at least ${rule.minLength} characters`;
}
if (rule.pattern && !rule.pattern.test(value)) {
return rule.patternMessage || `${field} format is invalid`;
}
return '';
};
const validateField = (field, value) => {
const error = validate(field, value);
setErrors(prev => ({ ...prev, [field]: error }));
return error === '';
};
const markTouched = (field) => {
setTouched(prev => ({ ...prev, [field]: true }));
};
return {
errors,
touched,
validateField,
markTouched,
isValid: Object.values(errors).every(error => !error)
};
};
// Usage
const AdvancedForm = () => {
const validationRules = {
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
patternMessage: 'Please enter a valid email'
},
password: {
required: true,
minLength: 8
}
};
const { errors, touched, validateField, markTouched, isValid } =
useFormValidation(validationRules);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<VStack spacing="lg">
<Input
label="Email"
value={email}
onChangeText={(value) => {
setEmail(value);
if (touched.email) validateField('email', value);
}}
onBlur={() => {
markTouched('email');
validateField('email', email);
}}
error={touched.email ? errors.email : ''}
state={touched.email && errors.email ? 'error' : 'default'}
/>
<Input
label="Password"
value={password}
onChangeText={(value) => {
setPassword(value);
if (touched.password) validateField('password', value);
}}
onBlur={() => {
markTouched('password');
validateField('password', password);
}}
isPasswordInput
error={touched.password ? errors.password : ''}
state={touched.password && errors.password ? 'error' : 'default'}
/>
</VStack>
);
};
const DynamicInputList = () => {
const [items, setItems] = useState([{ id: 1, value: '' }]);
const addItem = () => {
const newId = Math.max(...items.map(item => item.id)) + 1;
setItems(prev => [...prev, { id: newId, value: '' }]);
};
const removeItem = (id) => {
if (items.length > 1) {
setItems(prev => prev.filter(item => item.id !== id));
}
};
const updateItem = (id, value) => {
setItems(prev => prev.map(item =>
item.id === id ? { ...item, value } : item
));
};
return (
<VStack spacing="md" padding="lg">
<Text style={styles.sectionTitle}>Skills</Text>
{items.map((item, index) => (
<HStack key={item.id} spacing="sm" align="center">
<Input
placeholder={`Skill ${index + 1}`}
value={item.value}
onChangeText={(value) => updateItem(item.id, value)}
style={{ flex: 1 }}
variant="outline"
/>
<Button
variant="ghost"
size="sm"
onPress={() => removeItem(item.id)}
disabled={items.length === 1}
>
<ButtonIcon icon={<XIcon size={20} />} />
</Button>
</HStack>
))}
<Button
variant="outline"
onPress={addItem}
size="sm"
alignSelf="flex-start"
>
<ButtonIcon icon={<PlusIcon size={16} />} position="left" />
<ButtonText>Add Skill</ButtonText>
</Button>
</VStack>
);
};

The Input component features smooth animations that enhance user experience:

  • Focus Animations: Border color transitions, scale effects, and shadow animations
  • Label Animations: Floating label transitions with smooth scaling and positioning
  • Error Animations: Slide-in error messages with opacity transitions
  • Loading States: Subtle animation feedback during validation
<Input
label="Animated Input"
animated={true}
variant="primary"
placeholder="Focus to see animation"
/>

Form Design

  • Group related inputs logically and use consistent spacing
  • Provide clear labels and helpful placeholder text
  • Use appropriate input types (email, phone, etc.) for better UX
  • Implement real-time validation with clear error messages

Performance

  • Use controlled components sparingly for large forms
  • Implement debounced validation for expensive operations
  • Consider React.memo for input components in lists
  • Avoid creating new objects/functions in render methods

Validation UX

  • Show validation on blur rather than on every keystroke
  • Use positive reinforcement for successful validation
  • Group related validation errors together
  • Provide clear guidance on how to fix validation errors