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

Form Submit

Demonstrates async submit handling with loading, success, and error states.

See interactive examples below.

1import { createSignal, createMemo } from '@barefootjs/dom'2import { Input } from '@/components/ui/input'3import { Button } from '@/components/ui/button'4import { Toast, ToastProvider, ToastTitle, ToastDescription } from '@/components/ui/toast'56const [email, setEmail] = createSignal('')7const [loading, setLoading] = createSignal(false)8const [success, setSuccess] = createSignal(false)910const isValid = createMemo(() => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email()))1112const handleSubmit = async () => {13  if (!isValid() || loading()) return1415  setLoading(true)1617  try {18    await fetch('/api/subscribe', {19      method: 'POST',20      body: JSON.stringify({ email: email() })21    })22    setSuccess(true)23    setTimeout(() => setSuccess(false), 3000)24  } finally {25    setLoading(false)26  }27}2829<Input30  inputValue={email()}31  onInput={(e) => setEmail(e.target.value)}32  inputDisabled={loading()}33/>34<Button onClick={handleSubmit} disabled={!isValid() || loading()}>35  {loading() ? 'Submitting...' : 'Subscribe'}36</Button>37<ToastProvider>38  <Toast variant="success" open={success()}>39    <ToastTitle>Success</ToastTitle>40    <ToastDescription>Subscribed!</ToastDescription>41  </Toast>42</ToastProvider>

#Pattern Overview

Form submission in BarefootJS uses signals to manage async state transitions:idle → loading → success/error. This pattern provides reactive feedback without external state machines.

Key concepts:

  • Loading signal: Tracks submission in progress
  • Error signal: Stores error message from failed requests
  • Success signal: Triggers success feedback (toast, message)
  • Disabled state: Prevents double submission and shows loading

#Examples

#Basic Submit with Loading

1import { createSignal, createMemo } from '@barefootjs/dom'2import { Input } from '@/components/ui/input'3import { Button } from '@/components/ui/button'4import { Toast, ToastProvider, ToastTitle, ToastDescription } from '@/components/ui/toast'56const [email, setEmail] = createSignal('')7const [loading, setLoading] = createSignal(false)8const [success, setSuccess] = createSignal(false)910const isValid = createMemo(() => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email()))1112const handleSubmit = async () => {13  if (!isValid() || loading()) return1415  setLoading(true)1617  try {18    await fetch('/api/subscribe', {19      method: 'POST',20      body: JSON.stringify({ email: email() })21    })22    setSuccess(true)23    setTimeout(() => setSuccess(false), 3000)24  } finally {25    setLoading(false)26  }27}2829<Input30  inputValue={email()}31  onInput={(e) => setEmail(e.target.value)}32  inputDisabled={loading()}33/>34<Button onClick={handleSubmit} disabled={!isValid() || loading()}>35  {loading() ? 'Submitting...' : 'Subscribe'}36</Button>37<ToastProvider>38  <Toast variant="success" open={success()}>39    <ToastTitle>Success</ToastTitle>40    <ToastDescription>Subscribed!</ToastDescription>41  </Toast>42</ToastProvider>

#Network Error and Retry

1import { createSignal, createMemo } from '@barefootjs/dom'23const [loading, setLoading] = createSignal(false)4const [errorMsg, setErrorMsg] = createSignal('')5const [success, setSuccess] = createSignal(false)67const handleSubmit = async () => {8  setLoading(true)9  setErrorMsg('')1011  try {12    const res = await fetch('/api/submit', { method: 'POST', ... })13    if (!res.ok) throw new Error('Request failed')14    setSuccess(true)15  } catch (err) {16    setErrorMsg(err.message || 'Network error. Please try again.')17  } finally {18    setLoading(false)19  }20}2122const handleRetry = () => {23  setErrorMsg('')24  handleSubmit()25}2627<Toast variant="error" open={errorMsg() !== ''}>28  <ToastTitle>Error</ToastTitle>29  <ToastDescription>{errorMsg()}</ToastDescription>30  <ToastAction onClick={handleRetry}>Retry</ToastAction>31</Toast>

#Server Validation Error

Try "taken@example.com" to see server validation error

1import { createSignal, createMemo } from '@barefootjs/dom'23const [email, setEmail] = createSignal('')4const [serverError, setServerError] = createSignal('')56// Client-side validation7const clientError = createMemo(() => {8  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email())) return 'Invalid email'9  return ''10})1112const handleSubmit = async () => {13  const res = await fetch('/api/register', {14    method: 'POST',15    body: JSON.stringify({ email: email() })16  })1718  if (!res.ok) {19    const data = await res.json()20    // Display server validation error21    setServerError(data.error) // e.g., "Email already registered"22    return23  }2425  // Success handling...26}2728// Clear server error when user modifies input29<Input30  onInput={(e) => {31    setEmail(e.target.value)32    setServerError('')33  }}34/>35{serverError() && <p class="text-red-400">{serverError()}</p>}

#Key Points

Async State Management

  • Use loading signal to track submission state
  • Disable form inputs and button during submission
  • Show loading text in button: loading() ? "Submitting..." : "Submit"
  • Signals are sufficient for typical forms - no state machine needed

Error Handling

  • Store error message in signal: setErrorMsg(err.message)
  • Display errors via Toast (error variant) or inline message
  • Provide retry action for network errors
  • Clear error when user modifies input or retries

Success Feedback

  • Use Toast (success variant) for non-blocking feedback
  • Auto-dismiss success toast: setTimeout(() => setSuccess(false), 3000)
  • Reset form after successful submission if appropriate

Server Validation

  • Separate client-side and server-side validation signals
  • Display server errors inline near the relevant field
  • Clear server error when user modifies the field
  • Example: "Email already registered" from server response