Dyme

Input Components

Explore all input variants, states, and features from the design system.

API Reference

InputProps

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
}

PasswordInputProps

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
}

Automatic Variants (State-Based)

VariantWhen AppliedColorsPreview
defaultEmpty, not focused, no errorGray 100 bg, gray text
filledHas value OR is focusedLight blue (#D2F5FF) bg, primary text
errorerror prop is setGray 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.

Props Details

PropTypeDefaultDescription
labelstringundefinedLabel text displayed above input
errorstringundefinedError message shown below input. Triggers error variant with red border.
iconReactNodeundefinedIcon element to display (usually from lucide-react)
iconPosition'left' | 'right''right'Position of the icon relative to input field
containerClassNamestringundefinedAdditional CSS classes for the outer container div
inputSize'default''default'Size variant (currently only default: 48px height)
typestring'text'HTML input type (text, email, tel, password, etc.)
valuestringundefinedControlled input value. Triggers "filled" variant when not empty.
placeholderstringundefinedPlaceholder text shown when input is empty
onChangefunctionundefinedChange event handler
disabledbooleanfalseDisables the input field
showPasswordByDefaultbooleanfalse(PasswordInput only) Show password text initially

Important Notes

  • Input inherits all standard HTML input attributes
  • Variants (default, filled, error) are automatically applied based on state
  • The "filled" variant appears when input has a value OR is focused
  • Icons are automatically sized to 32px (h-8 w-8) containers
  • PasswordInput automatically includes Eye/EyeOff toggle button
  • Error messages are displayed below the input in red
  • Labels use extrabold font (800 weight) and gray-800 color
  • Input text is 20px (text-xl) for better mobile usability
  • The component maintains focus and blur handlers internally for state management

Basic Inputs

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"
/>

Password Input

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}
/>

Input States

Empty State

Filled State

Error State

Please enter a valid email address

Disabled State

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" />

Icon Positions

Icon Left

Icon Right

Without Labels

Form Example

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>
  )
}

Error Validation Example

This email is already taken
Password must be at least 8 characters
Please enter a valid phone number
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' : ''}
      />
    </>
  )
}

Interactive Demo

Type in the fields below to see the filled state

Accessibility Features

  • Labels: All inputs have associated labels for screen readers
  • Error Messages: Error states are clearly indicated with color and text
  • Focus States: Inputs show filled state on focus for better UX
  • Password Toggle: Eye icon is keyboard accessible (though tabIndex=-1 for form flow)
  • Placeholders: Clear placeholder text for better guidance