The submit handler.
Form
Create fully functional React forms with just a few lines of code.
The Form component is an abstraction around React Hook Form and follows the WAI specifications for forms.
Import#
Form
: The wrapper component provides context, state, and focus management.FormLayout
: Create consistent field spacing and positioning.Field
: Renders a fully functional form control, supports multiple types. Must be a child ofForm
.DisplayIf
: Conditionally render parts of a form.SubmitButton
: A button with typesubmit
and default color schemeprimary
andisLoading
state when the form is submitting.
import {Form,FormLayout,Field,DisplayIf,SubmitButton,} from '@saas-ui/react'
Best practises#
Do
- Keep optional fields to a minimum.
- Make it clear which fields are required or optional.
- Group related information in sections to make forms easier to scan.
- Consider splitting up long forms into multiple steps.
- Position the submit button consistenly throughout all forms.
Don't
- Use too many ungrouped fields on a single page.
- Use tabs inside forms.
Usage#
Basic form#
function BasicForm() {const onSubmit = (params) => {console.log(params)return new Promise((resolve) => {setTimeout(resolve, 1000)})}return (<FormdefaultValues={{name: 'Saas UI',description: '',}}onSubmit={onSubmit}><FormLayout><Fieldname="name"label="Name"type="text"help="Choose a name for this project"rules={{ required: true }}/><Fieldname="description"type="textarea"label="Description"placeholder="Optional description"/><SubmitButton>Create Project</SubmitButton></FormLayout></Form>)}
Schema resolvers#
Form
supports all React Hook Form resolvers out of the box.
function CreateProject() {const schema = Yup.object().shape({name: Yup.string().required().label('Name'),description: Yup.string().label('Description').min(50),})const onSubmit = (params) => {console.log(params)return new Promise((resolve) => {setTimeout(resolve, 1000)})}return (<FormdefaultValues={{name: '',description: '',}}resolver={yupResolver(schema)}onSubmit={onSubmit}><FormLayout><Fieldname="name"label="Name"type="text"help="Choose a title for this project."/><Fieldname="description"type="textarea"label="Description"help="Minimum 50 characters."/><SubmitButton>Create Project</SubmitButton></FormLayout></Form>)}
Disable the submit button when untouched#
<Form><FormLayout><Fieldname="title"label="Title"rules={{ required: 'Title is required' }}/><Field name="description" type="textarea" label="Description" /><SubmitButton disableIfUntouched /></FormLayout></Form>
Disable the submit button when invalid#
<Form onSubmit={saveHandler}><FormLayout><Fieldname="email"label="Email"rules={{ required: true, type: 'email' }}/><Fieldname="terms"type="checkbox"label="I accept the terms & conditions."rules={{ required: true }}/><SubmitButton disableIfInvalid /></FormLayout></Form>
Use your own submit button#
<Form onSubmit={saveHandler}><FormLayout><Field name="title" label="Title" /><Button type="submit" colorScheme="teal">Submit</Button></FormLayout></Form>
Group related fields#
<Form onSubmit={saveHandler}><FormLayout><Heading size="md">Personal information</Heading><FormLayout columns="2"><Field name="firstname" label="Name" /><Field name="lastname" label="Last name" /></FormLayout><Field name="email" label="Email address" /><Heading size="md" mt="4">Address</Heading><FormLayout><Field name="address" label="Address" /><Field name="city" label="City" /></FormLayout><Heading size="md" mt="4">Billing information</Heading><FormLayout columns="2"><Field name="card" label="Card number" /><FormLayout columns="2"><Field name="exp" label="Expiration date" /><Field name="cvc" label="CVC" /></FormLayout></FormLayout><SubmitButton>Complete order</SubmitButton></FormLayout></Form>
Conditionally show fields#
<Form onSubmit={saveHandler}><FormLayout><Heading size="md">Personal information</Heading><FormLayout columns="2"><Field name="firstname" label="Name" /><Field name="lastname" label="Last name" /></FormLayout><Field name="email" label="Email address" /><Fieldname="ship"type="checkbox"value={true}label="Ship to my home address"/><DisplayIf name="ship" condition={(ship) => !!ship}><FormLayout><Heading size="md" mt="4">Address</Heading><FormLayout><Field name="address" label="Address" /><Field name="city" label="City" /></FormLayout><Heading size="md" mt="4">Billing information</Heading><FormLayout columns="2"><Field name="card" label="Card number" /><FormLayout columns="2"><Field name="exp" label="Expiration date" /><Field name="cvc" label="CVC" /></FormLayout></FormLayout></FormLayout></DisplayIf><SubmitButton>Complete order</SubmitButton></FormLayout></Form>
Access the form context#
You can get access to the form context using a render prop, the useFormContext
hook or the form ref.
Render prop#
<Form onSubmit={saveHandler}>{(form) => (<FormLayout><Field name="title" label="Title" /><SubmitButton>Submit</SubmitButton></FormLayout>)}</Form>
useFormContext#
function ResetButton() {const form = useFormContext()return <Button onClick={() => form.reset()}>Reset</Button>}
Form ref#
function MyForm() {const formRef = useRef(null)return (<Form ref={formRef} onSubmit={saveHandler}><FormLayout><Field name="title" label="Title" /><SubmitButton>Submit</SubmitButton><Button onClick={() => formRef.current.reset()}>Reset</Button></FormLayout></Form>)}
Configure a default resolver#
In case you are using schemas in all your forms it's possible to configure a default resolver, this way you can simply pass schemas directly to your forms and save a few lines of boilerplate code.
// Add this somewhere in the root of your project.import { Form } from '@saas-ui/react'import { yupResolver, yupFieldResolver } from '@saas-ui/forms/yup'import { AnyObjectSchema } from 'yup'Form.getResolver = (schema: AnyObjectSchema) => yupResolver(schema) // @hookform/resolversForm.getFieldResolver = (schema: AnyObjectSchema) => yupFieldResolver(schema) // AutoForm field resolver// You no longer need to set a resolver on each form, so you can do<Form schema={schema} />// instead of<Form resolver={yupResolver(schema)} />
Typescript support#
type PostInputs = {firstName: stringlastName: string}export const TypescriptForm = () => {return (<Form<PostInputs>defaultValues={{firstName: 'Eelco',lastName: 'Wiersma',}}onSubmit={onSubmit}><FormLayout><Field<PostInputs> name="firstName" label="First name" /><Field<PostInputs> name="lastName" label="Last name" /></FormLayout></Form>)}
Accessibility#
The Form
component wraps the children in a HTML <form>
element.
Keyboard Interaction#
Key | Action |
---|---|
Enter | Submit the form |
Props#
onSubmit
required
onSubmit
required
SubmitHandler<TFieldValues>
children
children
The form children, can be a render prop or a ReactNode.
MaybeRenderProp<UseFormReturn<TFieldValues, any>>
context
context
any
criteriaMode
criteriaMode
CriteriaMode
defaultValues
defaultValues
{ [x: string]: any; }
delayError
delayError
number
formRef
formRef
Ref on the HTMLFormElement.
RefObject<HTMLFormElement>
mode
mode
keyof ValidationMode
onChange
onChange
Triggers when any of the field change.
WatchObserver<TFieldValues>
onError
onError
Triggers when there are validation errors.
SubmitErrorHandler<TFieldValues>
ref
ref
ForwardedRef<UseFormReturn<TFieldValues, any>>
resolver
resolver
Resolver<TFieldValues, any>
reValidateMode
reValidateMode
"onBlur" | "onChange" | "onSubmit"
schema
schema
The form schema, currently supports Yup schema only.
any
shouldFocusError
shouldFocusError
boolean
shouldUnregister
shouldUnregister
boolean
shouldUseNativeValidation
shouldUseNativeValidation
boolean
Was this helpful?