Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
react-form
Advanced tools
React Form is a lightweight framework and utility for building powerful forms in React applications.
$ npm install react-form
What does React-Form look like? This is the shortest and most concise example we could think of. Looking for more detail? Dive deep with the Annotated Demo Example
import React from 'react'
import { Form, Text } from 'react-form'
const myForm = (
<Form
onSubmit={(values) => {
console.log('Success!', values)
}}
validate={({ name }) => {
return {
name: !name ? 'A name is required' : undefined
}
}}
>
{({submitForm}) => {
return (
<form onSubmit={submitForm}>
<Text field='name' />
<button type='submit'>Submit</button>
</form>
)
}}
</Form>
)
This is an annotated example of the demo, demonstrating a majority of React-Form's features.
import React from 'react'
import { Form, Text, Select, Textarea, Checkbox, Radio, NestedForm, FormError } from 'react-form'
const MyForm = (
<Form
onSubmit={(values) => {
console.log('Success!', values)
}}
// Let's give the form some default values
defaultValues={{
friends: []
}}
// Validating your form is super easy, just use the `validate` life-cycle method
validate={values => {
const { name, hobby, status, friends, address } = values
return {
name: !name ? 'A name is required' : undefined,
hobby: (hobby && hobby.length < 5) ? 'Your hobby must be at least 5 characters long' : false,
status: !status ? 'A status is required' : null,
friends: (!friends || !friends.length) ? 'You need at least one friend!' : friends.map(friend => {
const { name, relationship } = friend
return {
name: !name ? 'A name is required' : undefined,
relationship: !relationship ? 'A relationship is required' : undefined
}
}),
address: !address ? 'A valid address is required' : 0
}
}}
// `onValidationFail` is another handy form life-cycle method
onValidationFail={() => {
window.alert('There is something wrong with your form! Please check for any required values and try again :)')
}}
>
{({ values, submitForm, addValue, removeValue, getError }) => {
// A Form's direct child will usually be a function that returns a component
// This way you have access to form methods and form values to use in your component. See the docs for a complete list.
return (
// When the form is submitted, call the `sumbitForm` callback prop
<form onSubmit={submitForm}>
<div>
<h6>Full Name</h6>
<Text // This is the built-in Text formInput
field='name' // field is a string version of the field location
placeholder='Your name' // all other props are sent through to the underlying component, in this case an <input />
/>
</div>
<div>
<h6>Relationship Status</h6>
<Select // This is the built-in Select formInput
field='status'
options={[{ // You can ship it some options like usual
label: 'Single',
value: 'single'
}, {
label: 'In a Relationship',
value: 'relationship'
}, {
label: 'It\'s Complicated',
value: 'complicated'
}]}
/>
</div>
<div>
<h6>Short Bio</h6>
<Textarea // This is the built-in Textarea formInput
field='bio'
placeholder='Short Bio'
/>
</div>
{/* Arrays in forms are super easy to handle */}
<h6>Friends</h6>
{/* This is a custom form error for the root of the friends list (see validation function) */}
<FormError field='friends' />
<div className='nested'>
{!values.friends.length ? (
<em>No friends have been added yet</em>
) : values.friends.map((friends, i) => ( // Loop over the values however you'd like
<div key={i}>
<div>
<h6>Full Name</h6>
<Text
field={['friends', i, 'name']} // You can easily pass an array-style field path. Perfect for passing down as props or nested values
placeholder='Friend Name'
/>
</div>
<div>
<h6>Relationship</h6>
<Select
field={`friends.${i}.relationship`} // If you don't like arrays, you can also use a string template
options={[{
label: 'Friend',
value: 'friend'
}, {
label: 'Acquaintance',
value: 'acquaintance'
}, {
label: 'Colleague',
value: 'colleague'
}]}
/>
</div>
<button // This button will remove this friend from the `friends` field
type='button'
onClick={() => removeValue('friends', i)} // `removeValue` takes a field location for an array, and the index for the item to remove
>
Remove Friend
</button>
</div>
))}
</div>
<div>
<button // This button will add a new blank friend item to the `friends` field
type='button'
onClick={() => addValue('friends', {})} // `addValue` takes an array-like field, and the value to add
>
Add Friend
</button>
</div>
<div>
<h6>Address</h6>
{/* An address has a couple of parts to it, and will probably have its own validation function. */}
{/* Let's make it reusable by using a nested form */}
<NestedForm
field='address' // The results of this nested form will be set to this field value on this form.
>
{AddressForm} // This is our reusable address form (see below)
</NestedForm>
</div>
<div>
<label>
<Checkbox // This is the built-in checkbox formInput
field='createAccount'
/>
<span>Create Account?</span>
</label>
</div>
<div>
<h6>Notify me via</h6>
<radiogroup>
<label>
<Radio // This is the built-in radio formInput
field='notificationType'
value='email' // This is the value the field will be set to when this radio button is active
/>
<span>Email</span>
</label>
<label>
<Radio
field='notificationType'
value='text'
/>
<span>Text</span>
</label>
<label>
<Radio
field='notificationType'
value='phone'
/>
<span>Phone</span>
</label>
</radiogroup>
</div>
<br />
<br />
{/* // Since this is the parent form, let's put a submit button in there ;) */}
{/* // You can submit your form however you want, as long as you call the `submitForm` callback */}
<button>
Submit
</button>
</form>
)
}}
</Form>
)
// This is our reusable address form
const AddressForm = (
<Form
// It can have its own validation function too! This keeps our parent validation function clean and flat
validate={values => {
return {
street: !values.street ? 'A street is required' : undefined,
city: !values.city ? 'A city is required' : undefined,
state: !values.state ? 'A state is required' : undefined
}
}}
>
// If you don't need access to any form methods or props, you can simply return JSX without wrapping it in a function
<Text
field='street'
placeholder='Street'
/>
<br />
<Text
field='city'
placeholder='City'
/>
<br />
<Text
field='state'
placeholder='State'
/>
</Form>
)
Creating custom form inputs is extremely simple. This is a quick example of how to wrap the React-Select component:
// formReactSelect.js
import React from 'react'
import { FormInput } from 'react-form'
import Select from 'react-select'
export default ({field, ...rest}) => {
return (
<FormInput field={field}> // Use FormInput with a fieldName to get the field's api
// FormInput's only child should be a function that provides you the field api and returns valid jsx or a react component
{({ setValue, getValue, setTouched }) => {
return (
<Select
{...rest} // Send the rest of your props to React-Select
value={getValue()} // Set the value to the forms value
onChange={val => setValue(val)} // On Change, update the form value
onBlur={() => setTouched()} // And the same goes for touched
/>
)
}}
</FormInput>
)
}
// Now you can use it in a form!
import React from 'react'
import FormReactSelect from './formReactSelect'
const myForm = (
<Form>
<FormReactSelect
field='my.field'
clearable={false}
options={[...]}
/>
</Form>
})
Example
import { Form } from 'react-form'
// Create a new form by passing `Form` and Form props
const myForm = (
<Form
onSubmit={}
validate={() => {...}}
// any other props
>
...
</Form>
Everything in react-form is configurable with the following props.
Note: You can also change the default props for every form component via FormDefaultProps
(See {FormDefaultProps})
<Form
defaultValues={{
name: 'Tanner Linsley'
hobby: 'javascript'
}}
>
loadState
is called. If a saved form state object is returned, it will hydrate the form state from this object.<Form
preValidate={(values) => {
values.hobby = values.hobby.substring(0, 6) // Constantly scrub the hobby field to never be more than 6 characters long
return values
}}
>
validate
returns any errors, setAllTouched(true)
will automatically be called on the form. Likewise, if the form has been marked as globally dirty, and no errors are returned, setAllTouched(false)
will automatically clear the global dirty state<Form
validate={({name, password}) => {
return {
name: !name ? 'A name is required' : null,
password: (!password || password.length < 6) ? 'A password of 6 or more characters is required' : null
}
}}
>
values
changeinitial
is set to true, this indicates that the component just mounted and is firing onChange
for the first time (this is utilized by nested forms)state
is changed.onSubmit
method.<Form
preSubmit={(values) => {
values.hobby = values.hobby.toLowerCase() // Scrub the hobby field to be lowercase on submit
return values
}}
>
submitForm
, this method will be called with the values of the parent form.// Per Instance (usually this way)
<Form
onSubmit={(values) => {
console.log('Form Submitted with these values:', values)
}}
/>
submitForm
, this method will be called.<Form
postSubmit={(values) => {
console.log('Success!', values)
}}
>
When a function is passed as the child of a form, it is passed the form API as a parameter. eg.
<Form>
{(api) => {
...
}}
</Form>
This also makes it extremely easy to destructure exactly what you need from the api!
<Form>
{({submitForm, values, addValue, removeValue}) => {
...
}}
</Form>
<Form>
{({values}) => {
console.log(values)
// {
// name: 'Tanner Linsley',
// hobby: 'Javascript',
// nestedForm: {
// notes: 'These are some notes from a nested form'
// }
// }
}}
</Form>
FormInput
or using the FieldError
component.validate
lifecycle methodnull
<Form>
{({errors}) => {
console.log(errors)
// {
// name: 'Name is required and must be at least 5 characters',
// hobby: 'Required',
// nestedForm: 'Requires some valid nested fields' // See "nestedErrors" section
// }
}}
</Form>
nestedErrors
is an object map indicating any nested forms that did not pass their own validationFormInput
or using the FieldError
component.nestedErrors
field will be set to true.{}
<Form>
{({nestedErrors}) => {
console.log(nestedErrors)
// {
// nestedForm: true // there must be an error in the form located at the "nestedForm" field
// }
}}
</Form>
setValue()
or setTouched()
, its corresponding field location in this object is marked as true.FormInput
or using the FieldError
component.{}
<Form>
{({touched}) => {
console.log(touched)
// {
// touched: { myField: true } // the `myField` field has been touched
// }
}}
</Form>
field
to value
. Usually this functionality is contained in an InputField of sorts, but it is still available on the form itself in its generic form.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
noTouch
is true, the touched
state of the field won't change.<Form>
{({setValue}) => {
return (
<button onClick={() => setValue('some.field', true)}>
Set Some Field to TRUE
</button>
)
}}
</Form>
field
. Usually this functionality is contained in an InputField of sorts, but it is still available on the form itself in its generic form.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
fallback
value which will be used if the field does not exist or if its value === undefined
<Form>
{({getValue}) => {
return (
<span>Current Value: {getValue('my.field')}</span>
)
}}
</Form>
field
in nestedErrors
to value
to indicate that a nested form did or did not not pass its own validation.validate
lifecycle method will be set to undefined.validate
lifecycle method.NestedForm
componentfield
. Usually this functionality is contained in an InputField of sorts, but it is still available on the form itself in its generic form.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({getError}) => {
return (
<span>Current Error: {getError('my.field')}</span>
)
}}
</Form>
field
in the touched
state to value
(which defaults to true). Usually this functionality is contained in an InputField of sorts, but it is still available on the form itself in its generic form.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({setTouched}) => {
return (
<button onClick={() => setTouched('some.field', true)}>
Set some fields touched value to TRUE
</button>
)
}}
</Form>
field
. Usually this functionality is contained in an InputField of sorts, but it is still available on the form itself in its generic form.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({getTouched}) => {
return (
<span>Current Touched: {getTouched('my.field')}</span>
)
}}
</Form>
value
into an array-like field
as a new value.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({addValue}) => {
return (
<button onClick={() => addValue('todos', {})}>
Add Todo
</button>
)
}}
</Form>
field
.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({removeValue}) => {
return (
<div>
{todos.map((todo, i) => {
return (
<button onClick={() => removeValue('todos', i)}>
Remove Todo
</button>
)
})}
</div>
)
}}
</Form>
field
.field
can be a string or array property location eg.
my.field.name
== ['my', 'field', 'name']
my.fields[3]
== ['my', 'fields', 3]
[['my', 'fields'], [5], 'stuff']
== my.fields[5].stuff
<Form>
{({swapValues}) => {
return (
<div>
{todos.map((todo, i) => {
return (
<button onClick={() => swapValues('todos', i, i - 1)}>
Move Up
</button>
)
})}
</div>
)
}}
</Form>
dirty
state to !!value
(which defaults to true
). If true
, its value will override any value retrieved using getTouched
.submitForm
lifecycle method when a forms validation is failed<Form>
{({setAllTouched}) => {
return (
<button onClick={setAllTouched}>
Touch / Dirty the entire form // innuendo? hmm....
</button>
)
}}
</Form>
preValidate(values, state, props)
filter your values before they are validatedvalidate(values, state, props)
validate your formonValidationFail(state, props)
handle failed validation (if applicable)preSubmit(values, state, props)
filter your values before they are successfully submittedonSubmit(values, state, props)
handle the submissionpostSubmit(values, state, props)
handle after a successful submission<Form
onSubmit={(values, state, props) => {
console.log(values)
}}
>
{({submitForm}) => {
return (
<form onSubmit={submitForm}>
<button type='submit'>Submit Form</button>
</form>
)
}}
</Form>
import { FormDefaultProps } from 'react-form'
Object.assign(FormDefaultProps, {
onValidationFail: () => { console.log('uh oh...') }
})
FormInput
must be a function that returns valid JSX. This function is passed one parameter, the form api object.field
prop is passed, every API method will be auto-bound to use that field
. Otherwise, they operate at the form-level as usual.showErrors
prop is set to false
, errors will be hiddenerrorBefore
prop is set to true
, any error will be shown before the component, instead of afterisForm
props is set to true
, the field error will only be shown if the parent form has been unsuccessfully submitted. This is normally only used for NestedForm
components to avoid unnecessary or duplicate error messages.Example - A simple wrapper for React Select
import { FormInput } from 'react-form'
import ReactSelect from 'react-select'
export default ({field, ...rest}) => {
return (
<FormInput field={field}>
{({ setValue, getValue, setTouched }) => {
return (
<ReactSelect
{...rest}
value={getValue()}
onChange={val => setValue(val)}
onBlur={() => setTouched()}
/>
)
}}
</FormInput>
)
}
Example
import { FormError } from 'react-form'
const example = (
<FormError field='my.field' />
)
FormField
must be a function that returns valid JSX. This function is passed one parameter, the form api object.field
prop is passed, every API method will be auto-bound to use that field. Otherwise, they operate at the form-level as usual.Example
import { FormField } from 'react-form'
function customInput ({field, ...rest}) {
return (
<FormField field={field}>
{({ setValue, getValue, setTouched }) => {
return (
<input
value={getValue()}
onChange={e => setValue(e.target.value)}
onBlur={() => setTouched()}
/>
)
}}
</FormField>
)
}
React-Form ships with plenty of standard form components and even provides an extremely easy way to create your own custom form components
FormInput
component.field
prop to be passedshowErrors
prop to false
to disableerrorBefore
prop to true
noTouch
to avoid validation while the value is changingExample
import { Text, Select, Checkbox, Textarea, Radio } from 'react-form'
const example = (
<div>
<Text
field='name'
placeholder='Full name'
/>
<Select
field='employed'
options={[{
label: 'Employed',
values: true
}, {
label: 'Unemployed',
value: false
}]}
/>
<Checkbox
field='createAccount'
/>
<Textarea
field='notes'
placeholder='Notes'
/>
<RadioGroup field='notificationType'>
<Radio value='email'/>
<Radio value='text'/>
<Radio value='phone'/>
</RadioGroup>
</div>
)
object
to their assigned field on their parent form.undefined
to their parent's validation function in place of the nested form's values. For instance, if a nested form using the field myNestedForm
has an error, the parent validation function might look like this: validate ({name, hobby, myNestedForm}) {
return {
// normal validation...
myNestedForm: !myNestedForm ? 'Name Required' : undefined,
}
}
<Form />
elements (which would be invalid HTML).onValidationFail
preSubmit
onSubmit
To suggest a feature, create an issue if it does not already exist. If you would like to help develop a suggested feature follow these steps:
npm install
npm watch
src/
directoryIf you would like to preview your changes, you can link and utilize the example like so:
npm link
cd example
npm install
npm link react-form
npm watch
src/screens/index.js
if neededlocalhost:8000
FAQs
⚛️ 💼 React hooks for managing form state and lifecycle
The npm package react-form receives a total of 6,751 weekly downloads. As such, react-form popularity was classified as popular.
We found that react-form demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.