Getting Started

Installation

With Yarn

yarn add @formiz/core @formiz/validations

Or with NPM

npm install @formiz/core @formiz/validations

Concept

The idea behind Formiz is to allow you to build advanced forms with multiple steps, complex validations and a good UX without pain.

The main idea is to build fields as independent reusable components. Fields can be anything, not just inputs. Once you have built your fields, you can use them everywhere.

When you use a field built with Formiz, you can apply validations rules on it. Only the mounted fields will apply their validation to the current step and to the form.

// Example
<MyField
name="email"
type="email"
required="Email is required"
validations={[
{
rule: isEmail(),
message: 'Not a valid email',
}
]}
/>

Formiz core does not provide any styles, so you can use it with any UI library and style you want. Use it with Chakra UI, ReactStrap, Material UI or your own styles.


Usage

Create your first custom field

A custom field can be anything, not just inputs.

Here there is an example with an input, but you can litteraly turn anything into a field and you will get validation for free!

1. Create a new field

Create a MyField.js file, then import the useField hook from @formiz/core

import React from 'react'
import { useField } from '@formiz/core'
export const MyField = () => {
// ...
}

2. Pass props to the hook

Get the props from the MyField component and pass it to the hook.

import React from 'react'
import { useField } from '@formiz/core'
export const MyField = (props) => {
const {} = useField(props) // Pass the props to the hook
// ...
}

3. Make it works

Create the <input> and make it interactive like this:

import React from 'react'
import { useField } from '@formiz/core'
export const MyField = (props) => {
const { setValue, value } = useField(props)
return (
<input
value={value ||ย ''} // Pass the value for the input
onChange={e => setValue(e.target.value)} // Update the value onChange
/>
)
}

3. Display error

Display the errorMessage below the <input>.

import React from 'react'
import { useField } from '@formiz/core'
export const MyField = (props) => {
const { errorMessage, isValid, setValue, value } = useField(props)
return (
<div>
<input
value={value ||ย ''}
onChange={e => setValue(e.target.value)}
/>
{
!isValid
&& errorMessage // Display error message
}
</div>
)
}

4. Improve UX and accessibility

  • Add a custom label and add * if the field is required.
  • Allow to change the input type.
  • Add css classes for style.
  • Display error only if the field is blured or the form/step is submitted.
  • Don't apply the is-error class if the input is focused.
  • Add id, for and aria attributes for accessibility.
import React from 'react'
import { useField } from '@formiz/core'
export const MyField = (props) => {
const {
errorMessage,
id,
isValid,
isSubmitted,
setValue,
value,
} = useField(props)
const { label, type, required } = props
const [isTouched, setIsTouched] = React.useState(false)
const showError = !isValid && (isTouched || isSubmitted)
return (
<div className={`demo-form-group ${(showError) ? 'is-error' : ''}`}>
<label
className="demo-label"
htmlFor={id}
>
{ label }
{!!required && ' *'}
</label>
<input
id={id}
type={type || 'text'}
value={value ||ย ''}
className="demo-input"
onChange={e => setValue(e.target.value)}
onBlur={() => setIsTouched(true)}
aria-invalid={showError}
aria-required={!!required}
aria-describedby={showError ? `${id}-error` : null}
/>
{showError && (
<div id={`${id}-error`} className="demo-form-feedback">
{ errorMessage }
</div>
)}
</div>
)
}

Use your field in a form

1. Create the form

import React from 'react'
import { Formiz } from '@formiz/core'
export const MyForm = () => {
return (
<Formiz>
{/* ... */}
</Formiz>
)
}

2. Connect the form with the useForm hook

import React from 'react'
import { Formiz, useForm } from '@formiz/core' // Import useForm
export const MyForm = () => {
const myForm = useForm() // Call useForm
return (
<Formiz
connect={myForm} // Connect to your form
>
{/* ... */}
</Formiz>
)
}

3. Handle submit

import React from 'react'
import { Formiz, useForm } from '@formiz/core'
export const MyForm = () => {
const myForm = useForm()
const handleSubmit = (values) => {
console.log(values) // Retrieves values after submit
}
return (
<Formiz
connect={myForm}
onValidSubmit={handleSubmit} // Handle submit only if the form is valid
>
<form // create an html form
noValidate // Disable native html validation
onSubmit={myForm.submit} // Pass the Formiz submit to the onSubmit
>
{/* Your fields here */}
<button
type="submit" // Create a submit button
disabled={!myForm.isValid} // Disable the button if the form is not valid
>
Submit
</button>
</form>
</Formiz>
)
}

4. Add fields to the form

Each field need a property "name" which will be the key in the object of values.
So, be sure this key is unique by form.

import React from 'react'
import { Formiz, useForm } from '@formiz/core'
import { isEmail } from '@formiz/validations' // Import some validations
import { MyField } from './MyField' // Import your field
export const MyForm = () => {
const myForm = useForm()
const handleSubmit = (values) => {
console.log(values)
}
return (
<Formiz
connect={myForm}
onValidSubmit={handleSubmit}
>
<form
noValidate
onSubmit={myForm.submit}
>
<MyField
name="firstName"
label="First Name"
required="First Name is required"
/>
<MyField
name="lastName"
label="Last Name"
required="Last Name is required"
/>
<MyField
name="email"
label="Email"
validations={[
{
rule: isEmail(),
message: 'This is not a valid email',
},
]}
/>
<button
type="submit"
disabled={!myForm.isValid}
>
Submit
</button>
</form>
</Formiz>
)
}

๐Ÿง™ Multi Steps form

Oh wait... you want to turn the form into a multi steps form?

Good news, that pretty easy!

You already have your fields so we just need to update the form to use steps.

Each FormizStep need a property "name", be sure this key is unique by form.

import React from 'react'
import {
Formiz,
FormizStep, // Import the FormizStep component
useForm,
} from '@formiz/core'
import { isEmail } from '@formiz/validations'
import { MyField } from './MyField'
export const MyForm = () => {
const myForm = useForm()
const handleSubmit = (values) => {
console.log(values)
}
return (
<Formiz
connect={myForm}
onValidSubmit={handleSubmit}
>
<form
noValidate
// Change the myForm.submit to myForm.submitStep
onSubmit={myForm.submitStep}
>
<FormizStep
name="step1" // Split the form with FormizStep
>
<MyField
name="firstName"
label="First Name"
required="First Name is required"
/>
<MyField
name="lastName"
label="Last Name"
required="Last Name is required"
/>
</FormizStep>
<FormizStep
name="step2" // Split the form with FormizStep
>
<MyField
name="email"
label="Email"
validations={[
{
rule: isEmail(),
message: 'This is not a valid email',
},
]}
/>
</FormizStep>
{/* Update the submit button to allow navigation between steps. */}
{!myForm.isFirstStep && (
<button type="button" onClick={myForm.prevStep}>
Back
</button>
)}
{myForm.isLastStep ? (
<button type="submit" disabled={!myForm.isValid}>
Submit
</button>
) : (
<button type="submit" disabled={!myForm.isStepValid}>
Continue
</button>
)}
</form>
</Formiz>
)
}

Magic

That's it ๐ŸŽ‰