Forms have been around since pretty much the beginning of the world wide web. And yet they seem to be very very hard to get just right for modern web apps. There are so many decision to make that might be the right choice in one use case but might lead to a very bad user experience in others:

Do you show validation errors before the user has typed anything? Do you show them if they are focusing the field? How about if they focused it once but didn’t type anything?

Should all validation errors behave the same? How about the ones sent from the server? And what about asynchronous validations?

Where do you show these validation errors? Next to input? Next to the submit button? On top of the form? A combination of these?

Do you save the data as the user is typing or leaving the input? Or is a classic button good enough? Do you maybe want to save unsubmitted changes in local storage?

Do you show labels above, or next to, or inside input fields?

What do you do to the form once the data is submitted and you’re waiting for a response?

To be blunt, I don’t have a good answer to all of these questions. And I don’t believe there exists a single set of answers that is equally valid for all use cases.

So I believe it’s quite futile to hope to be able to create the one form framework to rule them all.

Instead I decided to ask different questions:

How about providing a toolkit to assist you in defining your very own form behaviours?

And:

How about then using a minimal interface to describe your form contents using the behaviour you defined above?

As I couldn’t find anything satisfying my needs in the React ecosystem, I set out to create my own solution for our needs at Codecks and elsewhere. This resulted in developing React Reform.

The design process started out with defining a minimal form API that I’d be happy to use for all my forms:

< Form onSubmit = { this . handleSubmit } theme = " short " > < Text name = " email " label = " Your Email " isRequired isEmail /> < Password name = " password " label = " Your Password " isRequired /> </ Form >

So the theme describes the behaviour and looks, whereas the form’s children describe the content and validation constraints of the inputs.

Let’s have a look at how a basic theme can be defined:

const shortTheme = createTheme ( { renderForm : ( FormContainer , children , { isValid } ) => ( < FormContainer style = { { background : isValid ? 'green' : 'grey' } } > < div > { children } </ div > < button > Submit </ button > </ FormContainer > ) , renderField : ( Field , { directProps , name , isFocused , validations , id } ) => { const errors = validations . filter ( ( { isValid } ) => isValid === false ) . map ( ( { errorMessage , name } ) => < span key = { name } > { errorMessage } , </ span > ) return ( < div > < label htmlFor = { id } > { directProps . label || name } { ! isFocused && errors } </ label > < Field id = { id } style = { { background : isFocused ? 'lightgreen' : 'yellow' } } /> </ div > ) } )

So this is a two part process: first you define the behaviour and looks of the form body via renderForm , and set the behaviour and looks of each field via renderField . Rest assured that there are a lot more options to satisfy hopefully all of your needs.

React Reform allows you to define several themes for your app with differing looks and behaviours of which one can be defined as the default theme.

Controlled vs uncontrolled forms

Just like with inputs it’s sometimes desirable to lift the state of the form to the parent to gain more fine grained control over its values.

By default React Reform’s forms are uncontrolled, that is you only get access to the (validated) form values in the submit handler. Which is sufficient in many cases.

In case you need full knowledge of the form’s values you can pass a model and listen to changes via a onFieldChange handler. This allows you to e.g. externalise the state via redux or maybe store unsubmitted changes in local storage. See the Form docs for more details.

Wrapping custom input components

In many instances just using default HTML inputs isn’t enough. You want to use custom date pickers or colour selectors or whatnot. React Reform provides a <WrapInput> component that allows you to make your components compatible with React Reform.

In many cases it’s as straight forward as this:

const WrappedDatePicker = props => ( < WrapInput type = " DatePicker " directProps = { props } > { ( { value , themeProps } ) => ( < DatePicker value = { value || null } { ... themeProps } /> ) } </ WrapInput > )

In the docs I take a look at various date pickers and show how to wrap these including proper focus management. (I.e. allowing to focus or open the picker if it’s value is not valid)

Wrapping up

This pretty much sums up most of the special ingredients for React Reform. We’ve been using it at Codecks for over a year now and I couldn’t be happier! While your use cases certainly may differ I’ve found that React Reform offers all the flexibility I need while offering a really pleasant to use interface.

I’ve just completed a major rewrite based on all the lessons learned (which will be a topic for another blog post), and released v1.0 of React Reform including the launch of a proper homepage! In case you find any issue or have suggestions, please file an issue on the github repo.

If you found this post interesting, you might also want to follow me on twitter so I can keep you up to date about new posts :)