Form
Collect and submit user input
npm create webcore@latest add FormUse 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.
---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>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:
type: The type of the input, which can be a valid input type, including the following custom types:slider: Renders a range slider.switch: Renders a switch toggle that acts as a checkbox.textarea: Renders a textarea for multiline text input.
name: The name of the input that can be used for referencing the field during validation, change events, submission, and error reporting.
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:
---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:
const form = useForm('#form') // Select the form with the id of "form"const form = useForm(formElement) // Select the form using the form elementFrom here, you can attach various methods on the form variable to manipulate the form and its inputs. For example:
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 valuesgetInput, 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:
// Get the DOM element of the input whose name is set to "name"form.getInput('name')
// Get the value of the "email" inputform.getInputValue('email')
// Get the value of all inputsform.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:
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:
email: The email input should include “gmail”.message: The message field should be at least 20 characters long.
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.
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 => { ... })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:
form.reset() // Restores all inputs to their default valuesform.clear() // Empties all inputs regardless of their default valuesSmart 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:
useForm hook.- jane@doe.com
- +1 555-123-4567
- 2025-01-01
- https://jane@example.com
- 90210
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:
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:
- Override the following keys inside
patternsandinputs:email,phone,postal,date,url. - Change the behavior of your
useSmartFillfunction inside youruseForm.tsfile.
Destroying
When a form is removed from the DOM in an SPA, call destroy to remove all event listeners attached by 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'}FormField configurations. | Prop | Purpose |
|---|---|
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. |
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/Method | Purpose |
|---|---|
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. |