Form Validation
Demonstrates error state management and multi-field validation patterns using signals and memos.
See interactive examples below.
1import { createSignal, createMemo } from '@barefootjs/dom'2import { Input } from '@/components/ui/input'34const [name, setName] = createSignal('')5const [touched, setTouched] = createSignal(false)6const error = createMemo(() => {7 if (!touched()) return ''8 return name().trim() === '' ? 'Name is required' : ''9})1011<Input12 inputValue={name()}13 onInput={(e) => setName(e.target.value)}14 onBlur={() => setTouched(true)}15 inputPlaceholder="Enter your name"16/>17<p class="text-red-400">{error()}</p>#Pattern Overview
Form validation in BarefootJS uses createSignal for field values and createMemo for derived error states. This pattern provides reactive validation that automatically updates when field values change.
Key concepts:
- Field value signal: Stores the current input value
- Touched signal: Tracks if user has interacted with the field
- Error memo: Computes error message based on value and touched state
- Form validity memo: Computes overall form validity from all fields
#Examples
#Required Field
1import { createSignal, createMemo } from '@barefootjs/dom'2import { Input } from '@/components/ui/input'34const [name, setName] = createSignal('')5const [touched, setTouched] = createSignal(false)6const error = createMemo(() => {7 if (!touched()) return ''8 return name().trim() === '' ? 'Name is required' : ''9})1011<Input12 inputValue={name()}13 onInput={(e) => setName(e.target.value)}14 onBlur={() => setTouched(true)}15 inputPlaceholder="Enter your name"16/>17<p class="text-red-400">{error()}</p>#Email Format Validation
1import { createSignal, createMemo } from '@barefootjs/dom'23const [email, setEmail] = createSignal('')4const [touched, setTouched] = createSignal(false)5const error = createMemo(() => {6 if (!touched()) return ''7 if (email().trim() === '') return 'Email is required'8 if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email())) return 'Invalid email format'9 return ''10})11const isValid = createMemo(() => touched() && error() === '')1213<Input14 inputType="email"15 inputValue={email()}16 onInput={(e) => setEmail(e.target.value)}17 onBlur={() => setTouched(true)}18/>19<p class="text-red-400">{error()}</p>20{isValid() ? <span class="text-green-400">Valid</span> : null}#Password Confirmation
1import { createSignal, createMemo } from '@barefootjs/dom'23const [password, setPassword] = createSignal('')4const [confirmPassword, setConfirmPassword] = createSignal('')5const [passwordTouched, setPasswordTouched] = createSignal(false)6const [confirmTouched, setConfirmTouched] = createSignal(false)78const passwordError = createMemo(() => {9 if (!passwordTouched()) return ''10 if (password().length === 0) return 'Password is required'11 if (password().length < 8) return 'Password must be at least 8 characters'12 return ''13})1415const confirmError = createMemo(() => {16 if (!confirmTouched()) return ''17 if (confirmPassword().length === 0) return 'Please confirm your password'18 if (password() !== confirmPassword()) return 'Passwords do not match'19 return ''20})2122const isValid = createMemo(() =>23 passwordTouched() && confirmTouched() &&24 passwordError() === '' && confirmError() === ''25)#Multi-Field Form
1import { createSignal, createMemo } from '@barefootjs/dom'23// Field values4const [name, setName] = createSignal('')5const [email, setEmail] = createSignal('')6const [password, setPassword] = createSignal('')7const [confirmPassword, setConfirmPassword] = createSignal('')89// Touched states10const [nameTouched, setNameTouched] = createSignal(false)11// ... other touched states1213// Field validations (createMemo for each field)14const nameError = createMemo(() => {15 if (!nameTouched()) return ''16 if (name().trim() === '') return 'Name is required'17 if (name().trim().length < 2) return 'Name must be at least 2 characters'18 return ''19})20// ... other field validations2122// Form-level validity23const isFormValid = createMemo(() => {24 const nameValid = name().trim().length >= 225 const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email())26 const passwordValid = password().length >= 827 const confirmValid = password() === confirmPassword()28 return nameValid && emailValid && passwordValid && confirmValid29})3031const handleSubmit = () => {32 if (isFormValid()) {33 // Submit form34 }35}#Key Points
Error State Management
- Use
createSignalfor field values and touched states - Use
createMemofor computed error messages - Only show errors after field is touched (better UX)
- Return empty string for valid state, error message for invalid
Validation Timing
- On blur: Show errors when user leaves field (recommended)
- On submit: Validate all fields before form submission
- Real-time: For instant feedback (e.g., password strength)
Field Dependencies
- Access other field signals within a memo for cross-field validation
- Example:
password() !== confirmPassword() - The memo automatically re-evaluates when either signal changes
Form-Level Validity
- Combine field validations in a single
createMemo - Use for enabling/disabling submit button
- No need for a separate form library - signals are sufficient