Form Components

Comprehensive form components with validation, accessibility, and interactive examples

5 Form ControlsValidationWCAG Compliant

Text Input Variants

Standard States

We'll never share your email with anyone else.

Validation States

Looks good!

Please review this field

This field is required

Textarea Component

This field is required

Select Dropdown

Please select an option

Selection Controls

Checkboxes

Radio Buttons

Choose your plan:

Disabled options:

Code Examples

Basic Input with Validation
import { Input, FormLabel, FormError } from '~/components/ui';

function MyForm() {
  const [value, setValue] = useState('');
  const [error, setError] = useState('');

  const handleBlur = () => {
    if (!value.trim()) {
      setError('This field is required');
    } else {
      setError('');
    }
  };

  return (
    <div>
      <FormLabel htmlFor="input" required>
        Input Label
      </FormLabel>
      <Input
        id="input"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onBlur={handleBlur}
        error={error}
        placeholder="Enter text..."
      />
      {error && <FormError>{error}</FormError>}
    </div>
  );
}

Basic form input with label, validation, and error handling

Form Validation Hook
import { useState, useEffect } from 'react';
import { Input, FormLabel } from '~/components/ui';

function useFormValidation(initialValue: string, validator?: (value: string) => string | null) {
  const [value, setValue] = useState(initialValue);
  const [error, setError] = useState<string | null>(null);
  const [touched, setTouched] = useState(false);

  const handleChange = (newValue: string) => {
    setValue(newValue);
    if (touched && validator) {
      setError(validator(newValue));
    }
  };

  const handleBlur = () => {
    setTouched(true);
    if (validator) {
      setError(validator(value));
    }
  };

  return { value, error, touched, handleChange, handleBlur };
}

function MyForm() {
  const email = useFormValidation('', (value) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return value && !emailRegex.test(value) ? 'Invalid email' : null;
  });

  return (
    <div>
      <FormLabel htmlFor="email">Email</FormLabel>
      <Input
        id="email"
        value={email.value}
        onChange={(e) => email.handleChange(e.target.value)}
        onBlur={email.handleBlur}
        error={email.error || undefined}
      />
    </div>
  );
}

Custom hook for form validation with reusable logic

Complete Form with State
import { useState } from 'react';
import { Input, Textarea, Select, Checkbox } from '~/components/ui';

interface FormData {
  name: string;
  email: string;
  message: string;
  priority: string;
  newsletter: boolean;
}

function ContactForm() {
  const [formData, setFormData] = useState<FormData>({
    name: '',
    email: '',
    message: '',
    priority: 'medium',
    newsletter: false
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log('Form submitted:', formData);
    // Handle form submission
  };

  const updateField = (field: keyof FormData, value: string | boolean) => {
    setFormData(prev => ({ ...prev, [field]: value }));
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-6">
      <Input
        label="Name"
        value={formData.name}
        onChange={(e) => updateField('name', e.target.value)}
        required
      />

      <Input
        label="Email"
        type="email"
        value={formData.email}
        onChange={(e) => updateField('email', e.target.value)}
        required
      />

      <Select
        label="Priority"
        value={formData.priority}
        onChange={(e) => updateField('priority', e.target.value)}
      >
        <option value="low">Low</option>
        <option value="medium">Medium</option>
        <option value="high">High</option>
        <option value="urgent">Urgent</option>
      </Select>

      <Textarea
        label="Message"
        value={formData.message}
        onChange={(e) => updateField('message', e.target.value)}
        rows={4}
      />

      <Checkbox
        label="Subscribe to newsletter"
        checked={formData.newsletter}
        onChange={(checked) => updateField('newsletter', checked)}
      />

      <button
        type="submit"
        className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
      >
        Send Message
      </button>
    </form>
  );
}

Complete form implementation with TypeScript interfaces and state management

Form Best Practices

✅ Do's

  • Use clear, descriptive labels for all form fields
  • Provide helpful placeholder text and helper messages
  • Show validation errors immediately when fields are touched
  • Use proper input types (email, password, tel, etc.)
  • Group related fields and use logical tab order

❌ Don'ts

  • Don't rely on placeholder text as the only label
  • Avoid showing all validation errors at once initially
  • Don't disable form submission without clear indication
  • Avoid overly complex validation that frustrates users
  • Don't break the expected tab order for accessibility