Explore all input variants, states, and features from the design system.
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
// Label and Error
label?: string // Optional label text above input
error?: string // Error message (triggers error state)
// Icon
icon?: React.ReactNode // Icon element (e.g., from lucide-react)
iconPosition?: 'left' | 'right' // Icon position (default: 'right')
// Styling
containerClassName?: string // Additional classes for container div
inputSize?: 'default' // Size variant (currently only default: 48px)
// Standard HTML input props
type?: 'text' | 'email' | 'tel' | 'password' | ...
placeholder?: string
value?: string
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
disabled?: boolean
required?: boolean
// ... all other HTML input attributes
}interface PasswordInputProps extends Omit<InputProps, 'type' | 'icon'> {
// All InputProps except 'type' and 'icon' (automatically set)
showPasswordByDefault?: boolean // Whether to show password initially
// Note: type is always 'password' or 'text' (toggled by eye icon)
// Note: icon is always the Eye/EyeOff toggle button
}| Variant | When Applied | Colors | Preview |
|---|---|---|---|
| default | Empty, not focused, no error | Gray 100 bg, gray text | |
| filled | Has value OR is focused | Light blue (#D2F5FF) bg, primary text | |
| error | error prop is set | Gray 100 bg, red border + error text | Error message |
Note: Variants are automatically applied based on component state. You don't manually set variant props - the component handles this internally based onvalue,isFocused, anderror props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string | undefined | Label text displayed above input |
| error | string | undefined | Error message shown below input. Triggers error variant with red border. |
| icon | ReactNode | undefined | Icon element to display (usually from lucide-react) |
| iconPosition | 'left' | 'right' | 'right' | Position of the icon relative to input field |
| containerClassName | string | undefined | Additional CSS classes for the outer container div |
| inputSize | 'default' | 'default' | Size variant (currently only default: 48px height) |
| type | string | 'text' | HTML input type (text, email, tel, password, etc.) |
| value | string | undefined | Controlled input value. Triggers "filled" variant when not empty. |
| placeholder | string | undefined | Placeholder text shown when input is empty |
| onChange | function | undefined | Change event handler |
| disabled | boolean | false | Disables the input field |
| showPasswordByDefault | boolean | false | (PasswordInput only) Show password text initially |
import { Input } from '@/components/ui/input'
import { Mail, Search, Phone } from 'lucide-react'
// Basic input with label
<Input
label="Full Name"
placeholder="Enter your name"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
// With icon (right position by default)
<Input
label="Email Address"
type="email"
placeholder="your@email.com"
icon={<Mail className="h-5 w-5 text-[var(--color-gray-500)]" />}
/>
// With icon on left
<Input
label="Search"
placeholder="Search..."
icon={<Search className="h-5 w-5 text-[var(--color-gray-500)]" />}
iconPosition="left"
/>import { PasswordInput } from '@/components/ui/input'
// Basic password input with toggle
<PasswordInput
label="Password"
placeholder="Enter password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
// Show password by default
<PasswordInput
label="Password"
placeholder="Enter password"
showPasswordByDefault={true}
/>import { Input } from '@/components/ui/input'
// Empty state (default gray background)
<Input label="Username" placeholder="Enter username" />
// Filled state (light blue background, auto-applied when value exists)
<Input label="Username" value="johndoe" />
// Error state (red border, error message below)
<Input
label="Email"
error="Please enter a valid email address"
value="invalid-email"
/>
// Disabled state
<Input label="Field" disabled value="Cannot edit" />import { Input, PasswordInput } from '@/components/ui/input'
import { User, Mail } from 'lucide-react'
function RegistrationForm() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
// Handle form submission
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<Input
label="Full Name"
placeholder="John Doe"
required
icon={<User className="h-5 w-5 text-[var(--color-gray-500)]" />}
/>
<Input
label="Email Address"
type="email"
placeholder="john@example.com"
required
icon={<Mail className="h-5 w-5 text-[var(--color-gray-500)]" />}
/>
<PasswordInput
label="Password"
placeholder="Enter password"
required
/>
<PasswordInput
label="Confirm Password"
placeholder="Confirm password"
required
/>
<button type="submit">Create Account</button>
</form>
)
}import { Input, PasswordInput } from '@/components/ui/input'
import { useState } from 'react'
function MyForm() {
const [email, setEmail] = useState('')
const [emailError, setEmailError] = useState('')
const validateEmail = (value: string) => {
if (!value.includes('@')) {
setEmailError('Please enter a valid email address')
} else {
setEmailError('')
}
}
return (
<>
<Input
label="Email"
type="email"
value={email}
onChange={(e) => {
setEmail(e.target.value)
validateEmail(e.target.value)
}}
error={emailError}
/>
<PasswordInput
label="Password"
error={password.length < 8 ? 'Password must be at least 8 characters' : ''}
/>
</>
)
}Type in the fields below to see the filled state