Introduction
Accordion
Badge
Button
Card
Checkbox
Counter
Dialog
Dropdown
Input
Select
Switch
Tabs
Toast
Tooltip
Controlled Input
Field Arrays
Submit
Validation

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 createSignal for field values and touched states
  • Use createMemo for 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