# Input

> A styled text input component with label, validation, icons, and grouped layouts.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/components/input
- Markdown: https://ui.ahmedbna.com/docs/components/input.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/input.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/input.json
- Install: `npx bna-ui add input`
- npm dependencies: `lucide-react-native`, `react-native-svg`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`, `useColor`, `globals`, `text`, `view`, `icon`
- Preview recording: https://demo.ahmedbna.com/0175-input-demo.MP4

---

**Example:** A basic input with label and placeholder

```tsx
// components/demo/input/input-demo.tsx
import { Input } from '@/components/ui/input';
import { User } from 'lucide-react-native';
import React from 'react';

export function InputDemo() {
  return (
    <Input label='Username' placeholder='Enter your username' icon={User} />
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add input
```

### Manual

**1.** Install the following dependencies:

```bash
npx expo install lucide-react-native
```

**2.** Copy and paste the following code into your project.

```tsx
// components/ui/input.tsx
import { Icon } from '@/components/ui/icon';
import { Text } from '@/components/ui/text';
import { useColor } from '@/hooks/useColor';
import { BORDER_RADIUS, CORNERS, FONT_SIZE, HEIGHT } from '@/theme/globals';
import { LucideProps } from 'lucide-react-native';
import React, { forwardRef, ReactElement, useState } from 'react';
import {
  Pressable,
  TextInput,
  TextInputProps,
  TextStyle,
  View,
  ViewStyle,
} from 'react-native';

export interface InputProps extends Omit<TextInputProps, 'style'> {
  label?: string;
  error?: string;
  icon?: React.ComponentType<LucideProps>;
  rightComponent?: React.ReactNode | (() => React.ReactNode);
  containerStyle?: ViewStyle;
  inputStyle?: TextStyle;
  labelStyle?: TextStyle;
  errorStyle?: TextStyle;
  variant?: 'filled' | 'outline';
  disabled?: boolean;
  type?: 'input' | 'textarea';
  placeholder?: string;
  rows?: number; // Only used when type="textarea"
}

export const Input = forwardRef<TextInput, InputProps>(
  (
    {
      label,
      error,
      icon,
      rightComponent,
      containerStyle,
      inputStyle,
      labelStyle,
      errorStyle,
      variant = 'filled',
      disabled = false,
      type = 'input',
      rows = 4,
      onFocus,
      onBlur,
      placeholder,
      ...props
    },
    ref
  ) => {
    const [isFocused, setIsFocused] = useState(false);

    // Theme colors
    const cardColor = useColor('card');
    const textColor = useColor('text');
    const muted = useColor('textMuted');
    const borderColor = useColor('border');
    const primary = useColor('primary');
    const danger = useColor('red');

    const isTextarea = type === 'textarea';

    // Calculate height based on type
    const getHeight = () => {
      if (isTextarea) {
        return rows * 20 + 32; // Approximate line height + padding
      }
      return HEIGHT;
    };

    // Variant styles
    const getVariantStyle = (): ViewStyle => {
      const baseStyle: ViewStyle = {
        borderRadius: isTextarea ? BORDER_RADIUS : CORNERS,
        flexDirection: isTextarea ? 'column' : 'row',
        alignItems: isTextarea ? 'stretch' : 'center',
        minHeight: getHeight(),
        paddingHorizontal: 16,
        paddingVertical: isTextarea ? 12 : 0,
      };

      switch (variant) {
        case 'outline':
          return {
            ...baseStyle,
            borderWidth: 1,
            borderColor: error ? danger : isFocused ? primary : borderColor,
            backgroundColor: 'transparent',
          };
        case 'filled':
        default:
          return {
            ...baseStyle,
            borderWidth: 1,
            borderColor: error ? danger : cardColor,
            backgroundColor: disabled ? muted + '20' : cardColor,
          };
      }
    };

    const getInputStyle = (): TextStyle => ({
      flex: 1,
      fontSize: FONT_SIZE,
      lineHeight: isTextarea ? 20 : undefined,
      color: disabled ? muted : error ? danger : textColor,
      paddingVertical: 0, // Remove default padding
      textAlignVertical: isTextarea ? 'top' : 'center',
    });

    const handleFocus = (e: any) => {
      setIsFocused(true);
      onFocus?.(e);
    };

    const handleBlur = (e: any) => {
      setIsFocused(false);
      onBlur?.(e);
    };

    // Render right component - supports both direct components and functions
    const renderRightComponent = () => {
      if (!rightComponent) return null;

      // If it's a function, call it. Otherwise, render directly
      return typeof rightComponent === 'function'
        ? rightComponent()
        : rightComponent;
    };

    const renderInputContent = () => (
      <View style={containerStyle}>
        {/* Input Container */}
        <Pressable
          style={[getVariantStyle(), disabled && { opacity: 0.6 }]}
          onPress={() => {
            if (!disabled && ref && 'current' in ref && ref.current) {
              ref.current.focus();
            }
          }}
          disabled={disabled}
        >
          {isTextarea ? (
            // Textarea Layout (Column)
            <>
              {/* Header section with icon, label, and right component */}
              {(icon || label || rightComponent) && (
                <View
                  style={{
                    flexDirection: 'row',
                    alignItems: 'center',
                    marginBottom: 8,
                    gap: 8,
                  }}
                >
                  {/* Left section - Icon + Label */}
                  <View
                    style={{
                      flex: 1,
                      flexDirection: 'row',
                      alignItems: 'center',
                      gap: 8,
                    }}
                    pointerEvents='none'
                  >
                    {icon && (
                      <Icon
                        name={icon}
                        size={16}
                        color={error ? danger : muted}
                      />
                    )}
                    {label && (
                      <Text
                        variant='caption'
                        numberOfLines={1}
                        ellipsizeMode='tail'
                        style={[
                          {
                            color: error ? danger : muted,
                          },
                          labelStyle,
                        ]}
                        pointerEvents='none'
                      >
                        {label}
                      </Text>
                    )}
                  </View>

                  {/* Right Component */}
                  {renderRightComponent()}
                </View>
              )}

              {/* TextInput section */}
              <TextInput
                ref={ref}
                multiline
                numberOfLines={rows}
                style={[getInputStyle(), inputStyle]}
                placeholderTextColor={error ? danger + '99' : muted}
                placeholder={placeholder || 'Type your message...'}
                onFocus={handleFocus}
                onBlur={handleBlur}
                editable={!disabled}
                selectionColor={primary}
                accessibilityLabel={label}
                {...props}
              />
            </>
          ) : (
            // Input Layout (Row)
            <View
              style={{
                flexDirection: 'row',
                alignItems: 'center',
                gap: 8,
              }}
            >
              {/* Left section - Icon + Label (fixed width to simulate grid column) */}
              <View
                style={{
                  width: label ? 120 : 'auto',
                  flexDirection: 'row',
                  alignItems: 'center',
                  gap: 8,
                }}
                pointerEvents='none'
              >
                {icon && (
                  <Icon name={icon} size={16} color={error ? danger : muted} />
                )}
                {label && (
                  <Text
                    variant='caption'
                    numberOfLines={1}
                    ellipsizeMode='tail'
                    style={[
                      {
                        color: error ? danger : muted,
                      },
                      labelStyle,
                    ]}
                    pointerEvents='none'
                  >
                    {label}
                  </Text>
                )}
              </View>

              {/* TextInput section - takes remaining space */}
              <View style={{ flex: 1 }}>
                <TextInput
                  ref={ref}
                  style={[getInputStyle(), inputStyle]}
                  placeholderTextColor={error ? danger + 99 : muted}
                  onFocus={handleFocus}
                  onBlur={handleBlur}
                  editable={!disabled}
                  placeholder={placeholder}
                  selectionColor={primary}
                  accessibilityLabel={label}
                  {...props}
                />
              </View>

              {/* Right Component */}
              {renderRightComponent()}
            </View>
          )}
        </Pressable>

        {/* Error Message */}
        {error && (
          <Text
            style={[
              {
                marginLeft: 14,
                marginTop: 4,
                fontSize: 14,
                color: danger,
              },
              errorStyle,
            ]}
          >
            {error}
          </Text>
        )}
      </View>
    );

    return renderInputContent();
  }
);

export interface GroupedInputProps {
  children: React.ReactNode;
  containerStyle?: ViewStyle;
  title?: string;
  titleStyle?: TextStyle;
}

export const GroupedInput = ({
  children,
  containerStyle,
  title,
  titleStyle,
}: GroupedInputProps) => {
  const border = useColor('border');
  const background = useColor('card');
  const danger = useColor('red');

  const childrenArray = React.Children.toArray(children);

  const errors = childrenArray
    .filter(
      (child): child is ReactElement<any> =>
        React.isValidElement(child) && !!(child.props as any).error
    )
    .map((child) => child.props.error);

  const renderGroupedContent = () => (
    <View style={containerStyle}>
      {!!title && (
        <Text
          variant='title'
          style={[{ marginBottom: 8, marginLeft: 8 }, titleStyle]}
        >
          {title}
        </Text>
      )}

      <View
        style={{
          backgroundColor: background,
          borderColor: border,
          borderWidth: 1,
          borderRadius: BORDER_RADIUS,
          overflow: 'hidden',
        }}
      >
        {childrenArray.map((child, index) => (
          <View
            key={index}
            style={{
              minHeight: HEIGHT,
              paddingVertical: 12,
              paddingHorizontal: 16,
              justifyContent: 'center',
              borderBottomWidth: index !== childrenArray.length - 1 ? 1 : 0,
              borderColor: border,
            }}
          >
            {child}
          </View>
        ))}
      </View>

      {errors.length > 0 && (
        <View style={{ marginTop: 6 }}>
          {errors.map((error, i) => (
            <Text
              key={i}
              style={{
                fontSize: 14,
                color: danger,
                marginTop: i === 0 ? 0 : 1,
                marginLeft: 8,
              }}
            >
              {error}
            </Text>
          ))}
        </View>
      )}
    </View>
  );

  return renderGroupedContent();
};

export interface GroupedInputItemProps extends Omit<TextInputProps, 'style'> {
  label?: string;
  error?: string;
  icon?: React.ComponentType<LucideProps>;
  rightComponent?: React.ReactNode | (() => React.ReactNode);
  inputStyle?: TextStyle;
  labelStyle?: TextStyle;
  errorStyle?: TextStyle;
  disabled?: boolean;
  type?: 'input' | 'textarea';
  rows?: number; // Only used when type="textarea"
}

export const GroupedInputItem = forwardRef<TextInput, GroupedInputItemProps>(
  (
    {
      label,
      error,
      icon,
      rightComponent,
      inputStyle,
      labelStyle,
      errorStyle,
      disabled,
      type = 'input',
      rows = 3,
      onFocus,
      onBlur,
      placeholder,
      ...props
    },
    ref
  ) => {
    const [isFocused, setIsFocused] = useState(false);

    const text = useColor('text');
    const muted = useColor('textMuted');
    const primary = useColor('primary');
    const danger = useColor('red');

    const isTextarea = type === 'textarea';

    const handleFocus = (e: any) => {
      setIsFocused(true);
      onFocus?.(e);
    };

    const handleBlur = (e: any) => {
      setIsFocused(false);
      onBlur?.(e);
    };

    const renderRightComponent = () => {
      if (!rightComponent) return null;
      return typeof rightComponent === 'function'
        ? rightComponent()
        : rightComponent;
    };

    const renderItemContent = () => (
      <Pressable
        onPress={() => ref && 'current' in ref && ref.current?.focus()}
        disabled={disabled}
        style={{ opacity: disabled ? 0.6 : 1 }}
      >
        <View
          style={{
            flexDirection: isTextarea ? 'column' : 'row',
            alignItems: isTextarea ? 'stretch' : 'center',
            backgroundColor: 'transparent',
          }}
        >
          {isTextarea ? (
            // Textarea Layout (Column)
            <>
              {/* Header section with icon, label, and right component */}
              {(icon || label || rightComponent) && (
                <View
                  style={{
                    flexDirection: 'row',
                    alignItems: 'center',
                    marginBottom: 8,
                    gap: 8,
                  }}
                >
                  {/* Icon & Label */}
                  <View
                    style={{
                      flex: 1,
                      flexDirection: 'row',
                      alignItems: 'center',
                      gap: 8,
                    }}
                    pointerEvents='none'
                  >
                    {icon && (
                      <Icon
                        name={icon}
                        size={16}
                        color={error ? danger : muted}
                      />
                    )}
                    {label && (
                      <Text
                        variant='caption'
                        numberOfLines={1}
                        ellipsizeMode='tail'
                        style={[
                          {
                            color: error ? danger : muted,
                          },
                          labelStyle,
                        ]}
                        pointerEvents='none'
                      >
                        {label}
                      </Text>
                    )}
                  </View>

                  {/* Right Component */}
                  {renderRightComponent()}
                </View>
              )}

              {/* Textarea Input */}
              <TextInput
                ref={ref}
                multiline
                numberOfLines={rows}
                style={[
                  {
                    fontSize: FONT_SIZE,
                    lineHeight: 20,
                    color: disabled ? muted : error ? danger : text,
                    textAlignVertical: 'top',
                    paddingVertical: 0,
                    minHeight: rows * 20,
                  },
                  inputStyle,
                ]}
                placeholderTextColor={error ? danger + '99' : muted}
                placeholder={placeholder || 'Type your message...'}
                editable={!disabled}
                selectionColor={primary}
                onFocus={handleFocus}
                onBlur={handleBlur}
                accessibilityLabel={label}
                {...props}
              />
            </>
          ) : (
            // Input Layout (Row)
            <View
              style={{
                flex: 1,
                flexDirection: 'row',
                alignItems: 'center',
                gap: 8,
              }}
            >
              {/* Icon & Label */}
              <View
                style={{
                  width: label ? 120 : 'auto',
                  flexDirection: 'row',
                  alignItems: 'center',
                  gap: 8,
                }}
                pointerEvents='none'
              >
                {icon && (
                  <Icon name={icon} size={16} color={error ? danger : muted} />
                )}
                {label && (
                  <Text
                    variant='caption'
                    numberOfLines={1}
                    ellipsizeMode='tail'
                    style={[
                      {
                        color: error ? danger : muted,
                      },
                      labelStyle,
                    ]}
                    pointerEvents='none'
                  >
                    {label}
                  </Text>
                )}
              </View>

              {/* Input */}
              <View style={{ flex: 1 }}>
                <TextInput
                  ref={ref}
                  style={[
                    {
                      flex: 1,
                      fontSize: FONT_SIZE,
                      color: disabled ? muted : error ? danger : text,
                      paddingVertical: 0,
                    },
                    inputStyle,
                  ]}
                  placeholder={placeholder}
                  placeholderTextColor={error ? danger + '99' : muted}
                  editable={!disabled}
                  selectionColor={primary}
                  onFocus={handleFocus}
                  onBlur={handleBlur}
                  accessibilityLabel={label}
                  {...props}
                />
              </View>

              {/* Right Component */}
              {renderRightComponent()}
            </View>
          )}
        </View>
      </Pressable>
    );

    return renderItemContent();
  }
);
```

**3.** Update the import paths to match your project setup.

## Usage

```tsx
import { Input, GroupedInput, GroupedInputItem } from '@/components/ui/input';
```

```tsx
<Input label='Username' placeholder='Enter your username' icon={User} />
```

## Examples

#### Default

**Example:** A basic input with label and placeholder

```tsx
// components/demo/input/input-demo.tsx
import { Input } from '@/components/ui/input';
import { User } from 'lucide-react-native';
import React from 'react';

export function InputDemo() {
  return (
    <Input label='Username' placeholder='Enter your username' icon={User} />
  );
}
```

#### With Icons

**Example:** Inputs with left-side icons

```tsx
// components/demo/input/input-icons.tsx
import { Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
import { Lock, Mail, Phone, Search } from 'lucide-react-native';
import React from 'react';

export function InputIcons() {
  return (
    <View style={{ gap: 16 }}>
      <Input
        label='Email'
        placeholder='john@example.com'
        icon={Mail}
        keyboardType='email-address'
      />
      <Input
        label='Password'
        placeholder='Enter password'
        icon={Lock}
        secureTextEntry
      />
      <Input label='Search' placeholder='Search anything...' icon={Search} />
      <Input
        label='Phone'
        placeholder='+1 (555) 123-4567'
        icon={Phone}
        keyboardType='phone-pad'
      />
    </View>
  );
}
```

#### Variants

**Example:** Different input variants - filled and outline

```tsx
// components/demo/input/input-variants.tsx
import { Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
import { Mail, User } from 'lucide-react-native';
import React from 'react';

export function InputVariants() {
  return (
    <View style={{ gap: 16 }}>
      <Input
        variant='filled'
        label='Username'
        placeholder='Filled variant'
        icon={User}
      />
      <Input
        variant='outline'
        label='Email'
        placeholder='Outline variant'
        icon={Mail}
      />
    </View>
  );
}
```

#### Validation States

**Example:** Inputs with error states and validation messages

```tsx
// components/demo/input/input-validation.tsx
import { Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
import { Lock, Mail } from 'lucide-react-native';
import React, { useState } from 'react';

export function InputValidation() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const emailError =
    email && !email.includes('@') ? 'Please enter a valid email address' : '';
  const passwordError =
    password && password.length < 6
      ? 'Password must be at least 6 characters'
      : '';

  return (
    <View style={{ gap: 16 }}>
      <Input
        label='Email'
        placeholder='Enter your email'
        icon={Mail}
        value={email}
        onChangeText={setEmail}
        error={emailError}
        keyboardType='email-address'
      />
      <Input
        label='Password'
        placeholder='Enter password'
        icon={Lock}
        value={password}
        onChangeText={setPassword}
        error={passwordError}
        secureTextEntry
      />
    </View>
  );
}
```

#### Right Components

**Example:** Inputs with buttons, icons, or custom components on the right

```tsx
// components/demo/input/input-right-components.tsx
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { Copy, Eye, EyeOff, Search } from 'lucide-react-native';
import React, { useState } from 'react';
import { Pressable } from 'react-native';

export function InputRightComponents() {
  const muted = useColor('mutedForeground');

  const [copied, setCopied] = useState(false);
  const [showPassword, setShowPassword] = useState(false);

  const handleCopy = () => {
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <View style={{ gap: 16 }}>
      <Input
        label='Search'
        placeholder='Search with button...'
        icon={Search}
        rightComponent={
          <Button size='icon' variant='secondary'>
            <Text variant='caption'>Go</Text>
          </Button>
        }
      />

      <Input
        label='Password'
        placeholder='Toggle visibility'
        secureTextEntry={!showPassword}
        rightComponent={
          <Pressable onPress={() => setShowPassword(!showPassword)}>
            {showPassword ? (
              <EyeOff size={22} color={muted} />
            ) : (
              <Eye size={22} color={muted} />
            )}
          </Pressable>
        }
      />

      <Input
        label='API Key'
        placeholder='sk-1234567890abcdef'
        rightComponent={
          <Pressable onPress={handleCopy}>
            <View
              style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}
            >
              <Copy size={18} color={muted} />
              <Text variant='caption'>{copied ? 'Copied!' : 'Copy'}</Text>
            </View>
          </Pressable>
        }
      />
    </View>
  );
}
```

#### Disabled State

**Example:** Disabled inputs with reduced opacity

```tsx
// components/demo/input/input-disabled.tsx
import { Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
import { Mail, User } from 'lucide-react-native';
import React from 'react';

export function InputDisabled() {
  return (
    <View style={{ gap: 16 }}>
      <Input
        label='Username'
        placeholder='This input is disabled'
        icon={User}
        disabled
      />
      <Input label='Email' value='john@example.com' icon={Mail} disabled />
    </View>
  );
}
```

#### Grouped Inputs

**Example:** Multiple inputs grouped together in a card-like container

```tsx
// components/demo/input/input-grouped.tsx
import { GroupedInput, GroupedInputItem } from '@/components/ui/input';
import { Mail, MapPin, Phone, User } from 'lucide-react-native';
import React from 'react';

export function InputGrouped() {
  return (
    <GroupedInput title='Personal Information'>
      <GroupedInputItem label='Name' placeholder='John Doe' icon={User} />
      <GroupedInputItem
        label='Email'
        placeholder='john@example.com'
        icon={Mail}
        keyboardType='email-address'
      />
      <GroupedInputItem
        label='Phone'
        placeholder='+1 (555) 123-4567'
        icon={Phone}
        keyboardType='phone-pad'
      />
      <GroupedInputItem
        label='Address'
        placeholder='123 Main St'
        icon={MapPin}
      />
    </GroupedInput>
  );
}
```

#### Form Example

**Example:** Complete form example with various input types

```tsx
// components/demo/input/input-form.tsx
import { Button } from '@/components/ui/button';
import { GroupedInput, GroupedInputItem, Input } from '@/components/ui/input';
import { View } from '@/components/ui/view';
import {
  Calendar,
  CreditCard,
  Lock,
  Mail,
  Phone,
  User,
} from 'lucide-react-native';
import React, { useState } from 'react';

export function InputForm() {
  const [formData, setFormData] = useState({
    firstName: '',
    lastName: '',
    email: '',
    password: '',
    confirmPassword: '',
    phone: '',
    cardNumber: '',
    expiryDate: '',
    cvv: '',
  });

  const [errors, setErrors] = useState<Record<string, string>>({});

  const validateForm = () => {
    const newErrors: Record<string, string> = {};

    if (!formData.firstName) newErrors.firstName = 'First name is required';
    if (!formData.email) newErrors.email = 'Email is required';
    else if (!formData.email.includes('@'))
      newErrors.email = 'Invalid email format';
    if (!formData.password) newErrors.password = 'Password is required';
    else if (formData.password.length < 6)
      newErrors.password = 'Password must be at least 6 characters';
    if (formData.password !== formData.confirmPassword)
      newErrors.confirmPassword = 'Passwords do not match';

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = () => {
    if (validateForm()) {
      alert('Form submitted successfully!');
    }
  };

  return (
    <View style={{ gap: 24 }}>
      <GroupedInput title='Account Information'>
        <GroupedInputItem
          label='First Name'
          placeholder='John'
          icon={User}
          value={formData.firstName}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, firstName: text }))
          }
          error={errors.firstName}
        />
        <GroupedInputItem
          label='Last Name'
          placeholder='Doe'
          icon={User}
          value={formData.lastName}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, lastName: text }))
          }
        />
        <GroupedInputItem
          label='Email'
          placeholder='john@example.com'
          icon={Mail}
          value={formData.email}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, email: text }))
          }
          error={errors.email}
          keyboardType='email-address'
        />
        <GroupedInputItem
          label='Phone'
          placeholder='+1 (555) 123-4567'
          icon={Phone}
          value={formData.phone}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, phone: text }))
          }
          keyboardType='phone-pad'
        />
      </GroupedInput>

      <View style={{ gap: 16 }}>
        <Input
          label='Password'
          placeholder='Create password'
          icon={Lock}
          value={formData.password}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, password: text }))
          }
          error={errors.password}
          secureTextEntry
          variant='outline'
        />
        <Input
          label='Confirm Password'
          placeholder='Confirm password'
          icon={Lock}
          value={formData.confirmPassword}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, confirmPassword: text }))
          }
          error={errors.confirmPassword}
          secureTextEntry
          variant='outline'
        />
      </View>

      <GroupedInput title='Payment Information'>
        <GroupedInputItem
          label='Card Number'
          placeholder='1234 5678 9012 3456'
          icon={CreditCard}
          value={formData.cardNumber}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, cardNumber: text }))
          }
          keyboardType='numeric'
        />
        <GroupedInputItem
          label='Expiry Date'
          placeholder='MM/YY'
          icon={Calendar}
          value={formData.expiryDate}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, expiryDate: text }))
          }
          keyboardType='numeric'
        />
        <GroupedInputItem
          label='CVV'
          placeholder='123'
          value={formData.cvv}
          onChangeText={(text) =>
            setFormData((prev) => ({ ...prev, cvv: text }))
          }
          keyboardType='numeric'
        />
      </GroupedInput>

      <Button onPress={handleSubmit}>Submit Form</Button>
    </View>
  );
}
```

## API Reference

### Input

The main input component with label, validation, and icon support. Extends all `TextInputProps` except `style`.

| Prop             | Type                               | Default                                              | Description                                                                                                                             |
| ---------------- | ---------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `label`          | `string`                           | -                                                    | Label text displayed inside the input                                                                                                   |
| `error`          | `string`                           | -                                                    | Error message to display below the input                                                                                                |
| `icon`           | `React.ComponentType<LucideProps>` | -                                                    | Lucide icon component to display on the left                                                                                            |
| `rightComponent` | `ReactNode \| (() => ReactNode)`   | -                                                    | Component or function returning component for right side                                                                                |
| `containerStyle` | `ViewStyle`                        | -                                                    | Style for the outer container                                                                                                           |
| `inputStyle`     | `TextStyle`                        | -                                                    | Style for the text input                                                                                                                |
| `labelStyle`     | `TextStyle`                        | -                                                    | Style for the label text                                                                                                                |
| `errorStyle`     | `TextStyle`                        | -                                                    | Style for the error message                                                                                                             |
| `variant`        | `'filled' \| 'outline'`            | `'filled'`                                           | Visual variant of the input                                                                                                             |
| `disabled`       | `boolean`                          | `false`                                              | Whether the input is disabled                                                                                                           |
| `type`           | `'input' \| 'textarea'`            | `'input'`                                            | Switches the layout to a multiline textarea when set to `'textarea'`.                                                                   |
| `rows`           | `number`                           | `4`                                                  | Number of visible text lines. Only used when `type` is `"textarea"`.                                                                    |
| `placeholder`    | `string`                           | `'Type your message...'` when `type` is `"textarea"` | Placeholder text shown when the input is empty. `type="textarea"` falls back to a default when omitted; `type="input"` has no fallback. |

### GroupedInput

Container component for grouping multiple inputs together.

| Prop             | Type        | Description                                 |
| ---------------- | ----------- | ------------------------------------------- |
| `children`       | `ReactNode` | Child components (usually GroupedInputItem) |
| `containerStyle` | `ViewStyle` | Style for the container                     |
| `title`          | `string`    | Optional title for the group                |
| `titleStyle`     | `TextStyle` | Style for the title text                    |

### GroupedInputItem

Input component designed to be used within GroupedInput. Extends all `TextInputProps` except `style`.

| Prop             | Type                               | Default   | Description                                                           |
| ---------------- | ---------------------------------- | --------- | --------------------------------------------------------------------- |
| `label`          | `string`                           | -         | Label text displayed inside the input                                 |
| `error`          | `string`                           | -         | Error message (displayed at group level)                              |
| `icon`           | `React.ComponentType<LucideProps>` | -         | Lucide icon component to display on the left                          |
| `rightComponent` | `ReactNode \| (() => ReactNode)`   | -         | Component or function returning component for right side              |
| `inputStyle`     | `TextStyle`                        | -         | Style for the text input                                              |
| `labelStyle`     | `TextStyle`                        | -         | Style for the label text                                              |
| `errorStyle`     | `TextStyle`                        | -         | Style for the error message                                           |
| `disabled`       | `boolean`                          | `false`   | Whether the input is disabled                                         |
| `type`           | `'input' \| 'textarea'`            | `'input'` | Switches the layout to a multiline textarea when set to `'textarea'`. |
| `rows`           | `number`                           | `3`       | Number of visible text lines. Only used when `type` is `"textarea"`.  |

## Accessibility

The Input component is built with accessibility in mind:

- Proper focus management and keyboard navigation
- Screen reader support with semantic labeling
- High contrast support for error states
- Proper touch targets for mobile devices
- Support for dynamic text sizing
- Keyboard shortcuts and hardware keyboard support

## Styling

The Input component uses your theme colors and can be customized:

- `filled` variant: Uses card background with subtle borders
- `outline` variant: Transparent background with prominent borders
- Error states: Uses danger/red theme color
- Focus states: Uses primary theme color
- Disabled states: Reduced opacity with muted colors

## Best Practices

- Use clear, descriptive labels
- Provide helpful placeholder text
- Show validation errors immediately after user interaction
- Group related inputs using GroupedInput
- Use appropriate input types (email, password, etc.)
- Consider using icons to clarify input purpose
- Ensure sufficient contrast for accessibility
