🚀 Ship faster with premium components 🚀
Search documentation v1.4.0

Form

Collect and submit user input

How to add this block to your project
npm create webcore@latest add Form

Use the Form component to collect and submit user input. You can use the Form component to build forms, including but not limited to authentication (such as login, register, or password reset), submission (such as orders or feedback), or contact forms.

Example Form
How to use the Form component in Astro
---
const fields = [
{ type: 'text', label: 'Name', name: 'name' },
{ type: 'email', label: 'Email', name: 'email' },
{ type: 'checkbox', label: 'I accept the <a>terms and conditions</a>', name: 'terms' },
{ type: 'button', label: 'Subscribe' }
]
---
<Form fields={fields} id="form" />
<script>
import { toast } from 'webcoreui'
import { useForm } from '@blocks/Form/useForm'
const form = useForm('#form')
if (form) {
form.preventDefault()
.update('name', 'John Doe')
.update('email', 'john@doe.com')
.onChange(formValues => {
console.log('form values changed to:', formValues)
})
.onSubmit(form => {
toast({
element: '#toast',
content: `Submitting form with values: ${JSON.stringify(form)}`,
timeout: 3000
})
console.log('Submitting form with values:', form)
})
.onError(form => {
toast({
element: '#toast',
content: `Error submitting form. Invalid fields: ${JSON.stringify(form)}`,
timeout: 3000
})
console.log('Error submitting form. Invalid fields:', form)
})
}
</script>
You can find the documentation of the Toast component here to learn more about how to work with toasts.

The above example form can be used for collecting names and emails from users. To create a form, pass a fields prop to the component that specifies the input fields. Each field must have the following two properties:

Fields objects can be configured with additional properties. Each input type accepts different types of properties. For a complete list of documentation, please refer to the page of the individual input types:

You can specify the action prop to use the form as-is, or you can use the custom useForm hook provided with the form to handle submissions on the client.

Groups

You can group multiple inputs together to organize related inputs next to each other by using the group type for a field object:

Form with groups
How to group inputs
---
const fields = [
{
type: 'group',
fields: [
{ type: 'text', placeholder: 'Name' },
{ type: 'tel', placeholder: 'Phone' }
]
},
{ type: 'date', placeholder: 'Check-in' },
{
type: 'group',
fields: [
{ type: 'button', label: 'Send' },
{ type: 'button', label: 'Go Back', theme: 'secondary' }
]
}
]
---
<Form fields={fields} gap="sm" />

To create groups, specify the type of a field as group and add a fields property that takes in the same form as the fields prop itself. You can also change the gap around your inputs using the gap prop on the Form component.

useForm Hook

The Form component comes with a useForm hook that you can use to manipulate the form and listen to form events. To use the hook, call useForm with a selector or DOM element that selects the form that you want to use. In the above example, we used the id of the form:

Selecting forms using the useForm hook
const form = useForm('#form') // Select the form with the id of "form"
const form = useForm(formElement) // Select the form using the form element

From here, you can attach various methods on the form variable to manipulate the form and its inputs. For example:

How to manipulate forms using the useForm hook
form.preventDefault() // Prevent default form behavior
.useSmartFill() // Enable smart filling
.update('name', 'John Doe') // Update input fields with key-value pairs
.validate(formValues => ({ ... })) // Set validation rules
.onChange(formValues => { ... }) // Attach onChange event listeners
.onSubmit(form => { ... }) // Listen to events when the form is submitted
.onError(form => { ... }) // Listen to errors when a validation error occurs
.reset() // Restore all inputs to their default values
.clear() // Empty all inputs regardless of their default values
Note that the above methods can be chained, while getInput, getInputValue or getInputValues cannot be chained.

Accessing Input Fields

To access the value of input fields or the input fields themselves, you can use the following methods on the form:

How to access input fields using the hook
// Get the DOM element of the input whose name is set to "name"
form.getInput('name')
// Get the value of the "email" input
form.getInputValue('email')
// Get the value of all inputs
form.getInputValues()

Form Validation

The useForm hook can also be used for validation. To set validation rules, call the validate method and return a validation object:

How to validate forms
form.preventDefault()
.validate(formValues => ({
email: formValues.email.includes('gmail'), // Verify input named "email"
message: formValues.message?.length > 20 // Verify input named "message"
}))
.onSubmit(formValues => {
console.log('Submitting form with values:', formValues)
})
.onError(invalidFields => {
console.log('Invalid fields:', invalidFields)
})

The validate method expects a function that receives the current form values and returns a validation object where keys reference the input field’s name, and values are either true or false depending on their validity. The above rules expect that:

Submit Button Management

When validate is called, the hook automatically takes ownership of the form’s submit button and enables or disables it based on the current validation state. The button is updated both on initial evaluation and on every subsequent input change.

If a submit button already has a disabled attribute set explicitly, the hook will leave the button untouched. This signals that you want full manual control over that button.

Validation-driven submit button
form.validate(formValues => ({
email: formValues.email.includes('@'),
message: formValues.message?.length > 10
}))
.onChange() // onChange still needed to trigger re-evaluation on input events
.onSubmit(formValues => { ... })
Note that validate must be called before onChange in the chain.

Resetting and Clearing Forms

The hook provides two methods for emptying a form. Both re-evaluate validation rules and update the submit button state automatically if validate has been called:

How to reset and clear forms
form.reset() // Restores all inputs to their default values
form.clear() // Empties all inputs regardless of their default values

Smart Fill

You can use the useSmartFill method to automatically recognize and fill in multiple input fields at once on your forms when text is copied from other, unstructured sources. It currently supports emails, phone numbers, zip codes, dates, and urls:

Form with smart fill enabled
Copy the following text and paste it in the form next to it to test smart fill with the useForm hook.
How to enable smart fill
form.useSmartFill()

The function will try to infer input types from the input’s type and name attributes. If no match is found or the pasted text is too short, the browser’s default paste behavior will be used. To customize how different inputs are processed, you can pass a patterns and inputs parameter to the function:

How to customize smart filling
form.useSmartFill({
inputs: {
uid: 'user-id'
},
patterns: {
uid: /id-regex/
}
})

In this example, the function will look for an input whose name attribute is set to user-id and treat it as a uid type. This input will only be filled if patterns.uid matches the pattern from the pasted text. Note that keys inside patterns and inputs must match.

Specifying additional patterns and inputs will extend the default behavior. If you want to override the default handling of the existing types, you can either:

Destroying

When a form is removed from the DOM in an SPA, call destroy to remove all event listeners attached by the hook:

How to destroy the hook
form.destroy()

API

type FormField =
| ({ type: 'group', fields: FormField[] })
| ({ type: 'button', label: string } & ButtonProps)
| ({ type: 'checkbox' } & CheckboxProps)
| ({ type: 'radio' } & RadioProps)
| ({ type: 'select' } & SelectProps)
| ({ type: 'slider' } & SliderProps)
| ({ type: 'switch' } & SwitchProps)
| ({ type: 'textarea' } & TextareaProps)
| ({ type: NonNullable<InputProps['type']> } & Omit<InputProps, 'type'>)
type FormProps = {
fields: FormField[]
gap?: Gap
className?: string
id?: string
name?: string
action?: string
method?: 'post' | 'get' | 'dialog'
noValidate?: boolean
target?:
| '_self'
| '_blank'
| '_parent'
| '_top'
| '_unfencedTop'
enctype?:
| 'application/x-www-form-urlencoded'
| 'multipart/form-data'
| 'text/plain'
}
Please see the documentation of each input type below to learn more about FormField configurations.
PropPurpose
fields Sets the input fields for the form.
gap Sets the gap between the input fields.
className Sets a class for the form.
id Sets an id for the form.
name Sets the name attribute on the form.
action Sets the action attribute on the form.
method Sets the method attribute on the form.
noValidate Sets the form to noValidate.
target Sets the target attribute on the form.
enctype Sets the enctype attribute on the form.
JavaScript API
type ValidationFactory = (formValues: Record<string, string>) => Record<string, boolean>
type FormActions = {
validationRules: Record<string, boolean>
validationFactory: ValidationFactory | null
isPreventDefault: boolean
onErrorCallback: ((invalidFields: string[]) => void) | null
preventDefault: () => FormActions
getInput: (field: string) => HTMLInputElement | null
getInputValue: (field: string) => string | null
getInputValues: () => Record<string, string>
update: (field: string, value: string | boolean) => FormActions
reset: () => FormActions
clear: () => FormActions
destroy: () => void
validate: (validationFactory: ValidationFactory) => FormActions
isValid: () => boolean
useSmartFill: (options?: { patterns?: Record<string, RegExp>, inputs?: Record<string, string> }) => FormActions
onChange: (callback?: (formValues: Record<string, string>) => void) => FormActions
onSubmit: (callback: (formValues: Record<string, string>) => void) => FormActions
onError: (callback: (invalidFields: string[]) => void) => FormActions
}
Property/MethodPurpose
validationRules Returns the current validation state as evaluated by the function passed to validate.
validationFactory Returns the validation factory function set by validate, or null if not set.
isPreventDefault Returns a boolean whether preventDefault is set for the form.
onErrorCallback Returns the callback function specified by onError.
preventDefault() Calling the method sets preventDefault to true.
getInput() Pass the name of the input to return the DOM element for the input field.
getInputValue() Pass the name of the input to return its value, or null if the field does not exist.
getInputValues() Returns the values of all input fields from the form.
update() Sets a value for the specified input field.
reset() Resets all inputs to their default values and re-evaluates validation state.
clear() Empties all inputs regardless of their default values and re-evaluates validation state.
destroy() Removes all event listeners attached by the hook. Call this when the form is removed from the DOM.
validate() Sets a validation factory that validates the form. Automatically manages the submit button state.
isValid() Returns a boolean indicating whether the form is valid based on the current validation rules.
useSmartFill() Enable smart form fill when pasting blocks of text, such as from spreadsheets or documents.
onChange() Attach an optional event listener that triggers when any input changes.
onSubmit() Attach event listener to the form that triggers when the form is submitted.
onError() Attach event listener to the form when an error occurs due to a failed validation on submit.
UseForm