ReactJS forms made easy
⚠️ This repository is no longer maintained
Type less, do more. React forms made easy.
import { Form, Field } from "form-for";
const user = new User();
<Form for={user} onSubmit={...}>
<Field name="firstName" />
<Field name="lastName" />
<Field name="email" />
<Field name="password" />
<button>Submit</button>
</Form>
Just wanna play with it? Check out the demo
setCustomValidity
for better browser integrationpassword confirmation
npm install --save form-for
or https://unpkg.com/form-for/umd
Why there is no Redux binding?
Form states in general should not be managed by Redux. You'll most likely be just fine with the default state management. You can get the form data through onChange(data)
and onSubmit(event, data)
.
Forms are created based on a given schema. There are three ways to provide the form schema:
The @field
decorator may or may not have parameters.
import { field } from 'form-for';
export default class User {
@field name; // type defaults to 'text'
@field({ type: 'email', required: true })
email;
@field({ type: 'todoItem[]' })
todoItems;
}
const user = new User();
<Form for={user}>
<Field name="..."/>
</Form/>
export default class User {
schema = {
name: {}, // type defaults to 'text'
email: { type: 'email', required: true }
todoItems: { type: 'todoItem[]' }
};
}
const user = new User();
<Form for={user}>
<Field name="..."/>
</Form/>
<Form>
const schema = {
name: {}, // type defaults to 'text'
email: { type: 'email', required: true }
todoItems: { type: 'todoItem[]' }
}
const user = {};
<Form for={user} schema={schema}>
<Field name="..."/>
</Form/>
<Field>
Properties directly to the <Field>
tag override the schema properties.
<Form for={user} ...>
<Field name="..." type="special_type_for_this_form_only" placeholder="Special" />
</Form>
Note: Try avoiding this one, as it makes your forms longer and may lead to code duplication.
import { Field } from "form-for";
import React from "react";
class Component extends React.Component {
...
}
Field.connect('type', Component);
You can get the form data through onChange(data)
and onSubmit(event, data)
handleChange = data => {
// console.log(data);
};
handleSubmit = (event, data) => {
// console.log(data);
};
<Form onChange={handleChange} onSubmit={handleSubmit}>
...
</Form>;
Validation takes into consideration both custom validations and HTML 5 validations respectively.
You can make use of the HTML 5 validation attributes, such as required, min, max and minLength
. The HTML 5 validation messages are provided to the connected component, so it can display the error in a nice way.
const schema = { age: { type: 'number', max: 10, min: 2, required: true } };
<Form for={object} schema={schema}>
<Field name="age" required={true} />
</Form>;
import { field } from 'form-for';
export default class User {
@field({ error: 'validateName' })
name;
validateName(object, name) {
if (this.name === 'Nobody') return 'Nobody is not a name'; // 💎 Recommended
if (object[name] === 'Nobody') return 'Nobody is not a name'; // Same thing, but using the argument
// Returning undefined, false or null means there's no custom error, so form-for will check for HTML 5 errors
}
}
function validateAge(object, name) {
if (this.age === 999) return 'Are you this old???'; // 💎 Recommended
if (object[name] === 999) return 'Are you this old???'; // Same thing, but using the arguments
// Returning undefined, false or null means there's no custom error, so form-for will check for HTML 5 errors
}
<Field name="age" error={validateAge}>
this.state = { nameError: 'invalid name' };
<Field name="name" error={this.state.nameError}>
If for some reason you need to skip validations, just use the noValidate
prop.
<Form noValidate>...</Form>
The touched
property provided to a field component means that a field has been focused at least once. This is used to display error messages only after the user has gotten to an input.
There may be cases when you want to display the errors from the beginning, even before the user touches a field. For that, you can use touchOnMount
on <Form>
;
<Form touchOnMount>...</Form>
If you're using flow
for typing, you can import the component props: import type { ComponentProps } from "form-for";
.
PR's for Typescript typings are welcome.
These are the fields provided to a component: (the ? means it may or may not be provided)
type ComponentProps = {
name: string,
type?: string,
error?: string,
touched: boolean,
value: any,
onMount: Function,
onFocus: Function,
onChange: Function
};
Any other attributes provided through the <Field>
tag, @field
or the schema
will also be available through the props
.
Here's an example:
// @flow
import * as React from 'react';
import type { ComponentProps } from 'form-for';
export default class Input extends React.PureComponent<ComponentProps> {
input: ?HTMLInputElement;
componentDidMount() {
this.props.onMount(this.input);
}
render() {
const { error, ...props } = { ...this.props };
// onMount and touched are not used in this case, but they need to be deleted so they don't get passed down to the DOM
delete props.onMount;
delete props.touched;
return <input ref={el => (this.input = el)} aria-invalid={!!error} {...props} />;
}
}
These events must be psased down to the field, so form-for can properly handle the validations.
For all the events, if value and error are not provided they are guessed from the event.target
onMount(target: ?HTMLElement)
This event is used to setCustomValidity
, prevent the form from being submitted with pending custom validations and
allowing to focus on the field with error.
onFocus(event: Event)
onChange(event: Event, value?: any, error?: any)
If you're building a fancy component, such as a image cropper, you may need to provide the actual value
and error
,
unless these can be guessed from event.target
.
Here's an example: https://github.com/form-for/demo/blob/master/src/fields/Image/ImageField.js
It's recommend to look at the form-for-component-helpers package. It provides functions to facilitate creating components, specially when it comes to guessing labels.
form-for makes it a breeze to nest fields. You may have a User
class that has todoItems
, as list of TodoItem
instances.
Here's an example: https://github.com/form-for/demo/blob/master/src/fields/TodoItems/TodoItemsField.js
If you're using the MobX binding, please check https://github.com/form-for/form-for/tree/master/packages/mobx-form-for#nested-fields
All form-for packages are built with flow and provides support from the get go. Flow will automatically include typings when you import form-for modules. Although you do not need to import the types explicitly, you can still do it like this: import type { ... } from 'form-for'
.
To use the flow typings shipped with form-for packages:
.flowconfig
, you cannot ignore node_modules
..flowconfig
, you cannot import it explicitly in the [libs]
section.Typescript typings
mobx-state-tree binding
In depth blog post about form-for
More examples
Egghead.io course
The logo was created by Xicons.co and can be found here.