ExpoStartup

Input

A customizable text input component with floating label, error handling, and icon support.

Input

The Input component is a customizable text input field with a floating label animation, error handling, and support for right icons. It enhances the standard React Native TextInput with additional features and styling to create a cohesive and user-friendly form experience.

Features

  • Floating Label: Label animates from placeholder to top position when focused or filled
  • Error Handling: Displays error messages with distinct styling
  • Password Support: Toggle visibility option for password fields
  • Right Icon: Optional icon on the right side with customizable press behavior
  • Multiline Support: Can be used for both single-line and multiline text input
  • Customizable Styling: Control for input and container appearance
  • Dark Mode Support: Automatic light/dark mode styles

Import

import Input from '@/components/forms/Input';

Props

Core Props

PropTypeDefaultDescription
valuestringThe current value of the input
onChangeText(text: string) => voidFunction called when the text changes
labelstringText label for the input field
errorstringError message to display below the input

Additional Props

PropTypeDefaultDescription
isPasswordbooleanfalseIf true, treats the input as a password field with visibility toggle
rightIconIconNameIcon to display on the right side of the input
onRightIconPress() => voidFunction called when the right icon is pressed
isMultilinebooleanfalseIf true, enables multiline input mode
classNamestring''Additional Tailwind classes for the input
containerClassNamestring''Additional Tailwind classes for the container
...TextInputPropsAll standard React Native TextInput props are supported

Usage Examples

Basic Input

const [value, setValue] = useState('');
 
<Input
  label="Name"
  value={value}
  onChangeText={setValue}
/>

Password Input

const [password, setPassword] = useState('');
 
<Input
  label="Password"
  value={password}
  onChangeText={setPassword}
  isPassword={true}
/>

Input with Error

const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');
 
const validateEmail = (text) => {
  setEmail(text);
  if (!text.includes('@')) {
    setEmailError('Please enter a valid email address');
  } else {
    setEmailError('');
  }
};
 
<Input
  label="Email"
  value={email}
  onChangeText={validateEmail}
  error={emailError}
  keyboardType="email-address"
  autoCapitalize="none"
/>

Input with Right Icon

const [search, setSearch] = useState('');
 
<Input
  label="Search"
  value={search}
  onChangeText={setSearch}
  rightIcon="search"
  onRightIconPress={() => performSearch(search)}
/>

Multiline Input

const [bio, setBio] = useState('');
 
<Input
  label="Bio"
  value={bio}
  onChangeText={setBio}
  isMultiline={true}
/>

Custom Styled Input

<Input
  label="Custom Input"
  value={value}
  onChangeText={setValue}
  className="border-purple-500 focus:border-purple-700"
  containerClassName="mb-8"
/>

Form Example

const [formData, setFormData] = useState({
  name: '',
  email: '',
  password: '',
  bio: ''
});
const [errors, setErrors] = useState({});
 
const updateFormField = (field, value) => {
  setFormData({
    ...formData,
    [field]: value
  });
};
 
const validateForm = () => {
  let newErrors = {};
  
  if (!formData.name) {
    newErrors.name = 'Name is required';
  }
  
  if (!formData.email) {
    newErrors.email = 'Email is required';
  } else if (!formData.email.includes('@')) {
    newErrors.email = 'Please enter a valid email';
  }
  
  if (!formData.password) {
    newErrors.password = 'Password is required';
  } else if (formData.password.length < 8) {
    newErrors.password = 'Password must be at least 8 characters';
  }
  
  setErrors(newErrors);
  return Object.keys(newErrors).length === 0;
};
 
const handleSubmit = () => {
  if (validateForm()) {
    // Submit the form
    console.log('Form data:', formData);
  }
};
 
return (
  <View className="p-4">
    <Input
      label="Full Name"
      value={formData.name}
      onChangeText={(text) => updateFormField('name', text)}
      error={errors.name}
    />
    
    <Input
      label="Email Address"
      value={formData.email}
      onChangeText={(text) => updateFormField('email', text)}
      error={errors.email}
      keyboardType="email-address"
      autoCapitalize="none"
    />
    
    <Input
      label="Password"
      value={formData.password}
      onChangeText={(text) => updateFormField('password', text)}
      error={errors.password}
      isPassword={true}
    />
    
    <Input
      label="Bio"
      value={formData.bio}
      onChangeText={(text) => updateFormField('bio', text)}
      isMultiline={true}
    />
    
    <Button
      title="Submit"
      onPress={handleSubmit}
      className="mt-4"
    />
  </View>
);

Best Practices

Clear Labels

Use descriptive labels that clearly indicate what information is required:

// Good: Clear, descriptive labels
<Input label="Email Address" />
<Input label="Shipping Address" />
 
// Avoid: Vague or abbreviated labels
<Input label="Email" />
<Input label="Address" />

Contextual Keyboard Types

Set the appropriate keyboard type for different input fields:

// Email input
<Input
  label="Email"
  keyboardType="email-address"
  autoCapitalize="none"
/>
 
// Numeric input
<Input
  label="Age"
  keyboardType="numeric"
/>
 
// Phone number input
<Input
  label="Phone Number"
  keyboardType="phone-pad"
/>

Immediate Validation Feedback

Provide immediate feedback on input validation:

const validatePasswordStrength = (password) => {
  setPassword(password);
  
  if (password.length === 0) {
    setPasswordError('');
  } else if (password.length < 8) {
    setPasswordError('Password must be at least 8 characters');
  } else if (!/[A-Z]/.test(password)) {
    setPasswordError('Password must contain an uppercase letter');
  } else if (!/[0-9]/.test(password)) {
    setPasswordError('Password must contain a number');
  } else {
    setPasswordError('');
  }
};
 
<Input
  label="Password"
  value={password}
  onChangeText={validatePasswordStrength}
  error={passwordError}
  isPassword={true}
/>

Implementation Details

The Input component uses React Native's TextInput, View, and Animated components to create a polished input experience. The floating label is implemented using an animated value that transitions based on focus state and input value.

For password inputs, the component automatically adds a toggle icon to show/hide the password text.

The component uses the useThemeColors hook to apply appropriate color schemes for both light and dark modes.

Notes

  • The floating label animation occurs when the input is focused or has a value
  • The input border color changes based on focus state and error state
  • For password fields, a visibility toggle icon is automatically added
  • The component supports all standard TextInput props like placeholder, keyboardType, etc.
  • The input height adjusts automatically when in multiline mode
  • Clicking on the label will focus the input field

On this page