Build beautiful apps
Start

Input

The Input component allows users to enter text data in forms and interfaces. It provides a flexible and accessible way to collect user input with built-in support for validation, labels, icons, and various visual styles.

Import

import { Input } from '@flexi-ui/input'

Usage

Basic

A simple input field with a label and placeholder text.

With Description

Add helper text below the input to provide additional context or instructions.

Required

Mark an input as required with the isRequired prop. This adds a visual indicator and proper ARIA attributes.

Input Types

The Input component supports all standard HTML5 input types for different data formats.

All Input Types

Variants

Choose from four visual variants to match your design system.

Flat (Default)

The flat variant has a subtle background with no border.

Bordered

The bordered variant has a visible border around the input.

Faded

The faded variant has a semi-transparent background with a border.

Underlined

The underlined variant has only a bottom border.

All Variants

Compare all variants side by side.

Sizes

The Input component comes in three sizes: small, medium, and large.

Small

Medium (Default)

Large

All Sizes

Compare all sizes side by side.

Colors

Apply different color themes to inputs for various contexts.

Default

Primary

Secondary

Success

Warning

Error

All Colors

Compare all color variants side by side.

With Start and End Content

Add icons or other content at the start or end of the input field.

Start Content

End Content

Both Start and End Content

Multiple Icons

States

Disabled

Disable user interaction with the isDisabled prop.

Read-Only

Make an input read-only with the isReadOnly prop.

Required

Mark fields as required with the isRequired prop.

All States

Compare all states side by side.

Validation

Invalid State

Use the isInvalid prop to mark an input as invalid and display an error message.

Email Validation

Real-time email validation example.

Password Strength Validation

Password validation with strength indicator.

Form Validation

Complete form validation example.

Password Toggle

Create a password input with a toggle to show/hide the password.

Basic Password Toggle

Password Toggle with Validation

Controlled vs Uncontrolled

Controlled Input

The input value is controlled by React state.

Uncontrolled Input

The input manages its own state internally.

Controlled with Multiple Inputs

Custom Styling

Custom Classes

Use the classNames prop to customize specific parts of the input.

Custom Radius

Customize the border radius for different styles.

Styled with Tailwind

Advanced styling with Tailwind CSS.

Real-World Examples

Login Form

A complete login form with email and password inputs.

A search input with autocomplete suggestions.

Profile Editor

A complete profile editing form.

API Reference

Input Props

PropTypeDefaultDescription
labelstring-Label text displayed above the input
placeholderstring-Placeholder text shown when input is empty
descriptionstring-Helper text displayed below the input
errorMessagestring | ReactNode-Error message to display when input is invalid
valuestring-Controlled value of the input
defaultValuestring-Default value for uncontrolled input
typestring'text'HTML input type (text, email, password, number, url, tel, search, etc.)
namestring-Name attribute for form submission
size'sm' | 'md' | 'lg''md'Size of the input field
radius'none' | 'sm' | 'md' | 'lg' | 'full''md'Border radius of the input
variant'flat' | 'bordered' | 'faded' | 'underlined''flat'Visual style variant
color'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error''default'Color theme for the input
isRequiredbooleanfalseWhether the input is required
isReadOnlybooleanfalseWhether the input is read-only
isDisabledbooleanfalseWhether the input is disabled
isInvalidbooleanfalseWhether the input is in an invalid state
startContentReactNode-Content to display at the start of the input (e.g., icons)
endContentReactNode-Content to display at the end of the input (e.g., icons, buttons)
classNamesInputClassNames-Custom classes for different slots
classNamestring-Additional CSS class for the base element
autoFocusbooleanfalseWhether to auto-focus the input on mount
autoCompletestring-HTML autocomplete attribute
minnumber | string-Minimum value (for number/date inputs)
maxnumber | string-Maximum value (for number/date inputs)
minLengthnumber-Minimum length of input value
maxLengthnumber-Maximum length of input value
patternstring-Regex pattern for validation
onChange(e: ChangeEvent<HTMLInputElement>) => void-Change event handler
onFocus(e: FocusEvent<HTMLInputElement>) => void-Focus event handler
onBlur(e: FocusEvent<HTMLInputElement>) => void-Blur event handler
onKeyDown(e: KeyboardEvent<HTMLInputElement>) => void-Key down event handler
onKeyUp(e: KeyboardEvent<HTMLInputElement>) => void-Key up event handler

InputClassNames

Object for customizing component slots:

type InputClassNames = {
  base?: string           // Main wrapper element
  label?: string          // Label element
  inputWrapper?: string   // Input wrapper (contains input and icons)
  input?: string          // Input element
  description?: string    // Description text element
  errorMessage?: string   // Error message element
}

Slots

The Input component has the following slots that can be styled:

  • base - The main wrapper containing all elements
  • label - The label element displayed above the input
  • inputWrapper - The wrapper containing the input and start/end content
  • input - The actual input element
  • description - The description text displayed below the input
  • errorMessage - The error message text displayed when invalid

Accessibility

The Input component follows WAI-ARIA best practices for form inputs:

Keyboard Navigation

  • Tab - Move focus to/from the input
  • Shift + Tab - Move focus backwards
  • All standard input shortcuts - Copy (Ctrl+C), Paste (Ctrl+V), Select All (Ctrl+A), etc.

Screen Reader Support

  • Labels are automatically associated with inputs using proper htmlFor and id attributes
  • Required fields are announced with aria-required="true"
  • Invalid fields are announced with aria-invalid="true"
  • Error messages are linked using aria-describedby
  • Disabled state is communicated with aria-disabled="true"
  • Description text is associated using aria-describedby

ARIA Attributes

The component automatically manages these ARIA attributes:

  • aria-label - When no visible label is provided
  • aria-labelledby - Associates the input with its label
  • aria-describedby - Links description and error messages
  • aria-required - Indicates required fields
  • aria-invalid - Indicates validation state
  • aria-disabled - Indicates disabled state

Focus Management

  • Clear focus indicators for keyboard navigation
  • Focus is properly managed when using autoFocus
  • Focus states are visible and meet WCAG contrast requirements

Best Practices

  1. Always provide labels - Use the label prop or aria-label for screen readers
  2. Use appropriate input types - Helps with mobile keyboards and browser validation
  3. Provide clear error messages - Be specific about what went wrong and how to fix it
  4. Mark required fields - Use isRequired to indicate mandatory fields
  5. Use descriptions for additional context - Help users understand what's expected
  6. Ensure sufficient color contrast - Don't rely solely on color to convey state

Best Practices

1. Use Appropriate Input Types

Choose the correct input type for the data you're collecting to provide better user experience and mobile keyboard optimization:

<Input type="email" label="Email" />  // Shows @ on mobile keyboards
<Input type="tel" label="Phone" />    // Shows numeric keypad
<Input type="url" label="Website" />  // Shows .com on mobile keyboards
<Input type="number" label="Age" />   // Shows number pad

2. Provide Clear Labels and Descriptions

Always include descriptive labels and use descriptions to provide additional context:

<Input
  label="Password"
  description="Must be at least 8 characters with mixed case and numbers"
  type="password"
/>

3. Implement Real-Time Validation

Validate user input as they type to provide immediate feedback:

const [email, setEmail] = useState('')
const isInvalid = email !== '' && !email.includes('@')
 
<Input
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  isInvalid={isInvalid}
  errorMessage={isInvalid && "Please enter a valid email"}
/>

4. Use Icons for Visual Clarity

Add icons to help users quickly identify the purpose of each field:

<Input
  label="Email"
  startContent={<Mail className="w-4 h-4 text-gray-400" />}
/>

5. Handle Loading and Disabled States

Disable inputs during form submission or loading states:

<Input
  label="Username"
  isDisabled={isSubmitting}
  description={isSubmitting ? "Saving..." : undefined}
/>

Use consistent sizing and styling for related form fields:

<div className="space-y-4">
  <Input size="md" label="First Name" />
  <Input size="md" label="Last Name" />
  <Input size="md" label="Email" type="email" />
</div>

7. Implement Proper Error Handling

Show specific, actionable error messages:

// ❌ Bad
<Input isInvalid errorMessage="Invalid input" />
 
// ✅ Good
<Input
  isInvalid
  errorMessage="Email must include @ symbol and domain name"
/>

8. Use Autocomplete Attributes

Help browsers autofill forms correctly:

<Input label="Email" type="email" autoComplete="email" />
<Input label="Password" type="password" autoComplete="current-password" />
<Input label="New Password" type="password" autoComplete="new-password" />

9. Consider Mobile UX

Ensure inputs are properly sized for touch targets (minimum 44x44px):

<Input size="lg" label="Mobile-Friendly Input" />

10. Provide Visual Feedback

Use colors and states to communicate the input status:

<Input
  color={isValid ? "success" : "default"}
  label="Username"
  description={isValid ? "Username available!" : "Enter a username"}
/>

Troubleshooting

Input value not updating

Problem: The input value doesn't change when typing.

Solution: Make sure you're using either controlled (with value and onChange) or uncontrolled (with defaultValue) pattern correctly:

// ✅ Controlled
const [value, setValue] = useState('')
<Input value={value} onChange={(e) => setValue(e.target.value)} />
 
// ✅ Uncontrolled
<Input defaultValue="initial value" />
 
// ❌ Don't mix them
<Input value={value} defaultValue="initial" />

Icons not displaying properly

Problem: Icons from startContent or endContent are misaligned or too large.

Solution: Ensure icons have proper sizing classes and use flexbox:

// ✅ Good
<Input
  startContent={<Mail className="w-4 h-4 text-gray-400" />}
/>
 
// ❌ Bad - no sizing
<Input
  startContent={<Mail />}
/>

Validation not working

Problem: Error messages don't appear or validation doesn't trigger.

Solution: Make sure to set both isInvalid and errorMessage props:

// ✅ Good
<Input
  isInvalid={hasError}
  errorMessage={hasError && "This field is required"}
/>
 
// ❌ Bad - missing isInvalid
<Input errorMessage="Error" />

AutoFocus not working

Problem: Input doesn't receive focus on page load.

Solution: Ensure autoFocus prop is set and there's only one autofocused element per page:

<Input label="Username" autoFocus />

Style overrides not applying

Problem: Custom styles via classNames prop aren't being applied.

Solution: Check that you're targeting the correct slot and using proper Tailwind classes:

// ✅ Correct slot targeting
<Input
  classNames={{
    input: 'text-blue-600',
    inputWrapper: 'border-2 border-blue-500',
  }}
/>
 
// ❌ Wrong approach
<Input className="text-blue-600" />  // Only styles base wrapper

Password toggle button not working

Problem: Clicking the eye icon doesn't toggle password visibility.

Solution: Ensure you're preventing default button behavior and managing state correctly:

const [isVisible, setIsVisible] = useState(false)
 
<Input
  type={isVisible ? 'text' : 'password'}
  endContent={
    <button
      type="button"  // Important: prevents form submission
      onClick={() => setIsVisible(!isVisible)}
    >
      {isVisible ? <EyeOff /> : <Eye />}
    </button>
  }
/>

Form submission issues

Problem: Form submits even when inputs are invalid.

Solution: Implement proper form validation before submission:

const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault()
 
  // Validate all fields first
  if (hasErrors) {
    return
  }
 
  // Then submit
  submitForm()
}

Number input accepts non-numeric characters

Problem: Users can type non-numeric characters in number inputs.

Solution: Use type="number" and optionally add additional validation:

<Input
  type="number"
  min={0}
  max={100}
  onChange={(e) => {
    const value = e.target.value
    if (value === '' || /^\d+$/.test(value)) {
      setValue(value)
    }
  }}
/>
  • Textarea - Multi-line text input for longer content
  • Select - Dropdown selection input
  • Checkbox - Binary selection input
  • Radio - Single selection from multiple options
  • Switch - Toggle between two states
  • Form - Form wrapper with validation
  • Button - Form submission and actions