Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

ReactJS Form Validation Approaches

Published on January 19, 2017
Last Updated on April 8, 2021
Application Development

As a web developer who is relatively new to ReactJS, I take for granted the built-in support for form validation that I’ve enjoyed in other JavaScript frameworks, like Angular and Sencha. What follows is an elaboration on this problem, as well as a discussion of two approaches to solving it in React, on which I experimented for the same basic form design.

So what do I mean by “support for form validation”? Angular 1, for example, affords us a form model with properties, like $valid, $invalid, $pristine, and $dirty, out of the box. These occur at two levels: at the level of form inputs and at the level of the entire form that contains them. They facilitate validation logic at both levels and displaying feedback to users. Although the framework leaves many UI decisions to designers and developers, such as how and where to use these properties, it also provides us with specific input-type validators, like date and email. Sencha’s classic ExtJS toolkit affords us even more support than Angular 1. Not only does it provide input-type validators, but also display themes that we can customize or replace with another design theme.

React alone is relatively bare-bones when it comes to supporting form validation. Of course, we can always fall back on whatever HTML5 “constraint validation” support the browser provides. For example, using the type, required, and pattern attributes on input[type="text"] elements and the :valid and :invalid CSS pseudo-classes. But we may very well want more control over validation than browser API’s alone afford us. This is especially so in single-page applications, where we don’t normally submit forms in the standard way (that is, the way to trigger localized error messages in the browser) and in legacy browsers. Typically, we add novalidate attribute to the form, cancel submit events via e.preventDefault(), and rely on JS to extend native browser capability. (See here for more on browser support and its limitations.)

Whereas the aforementioned frameworks come fully baked with this extension, whether we want it or not, React leaves us with the 3 options:

  • Rely on browser API’s
  • Code a JS solution from scratch
  • Install another JS library – ideally one that plays nicely with React

I decided to experiment with two of these approaches when developing the same form component in React, both of which are available in public code pens (see links below) and appear like so:

 

ReactJS Form Validation: Simple Form Animation

 

The first approach relies on the browser’s constraint validation API and actually provides a lot of control via the ValidityState object and setCustomValidity method, which occur in modern browsers on each input element. The custom showInputError and showFormErrors functions require more coding than we might be accustomed to, but the standard API has already taken care of most of the validation logic for us, so we’re simply checking that the passwords match and deciding what error messages to show based on the standard API.

if (isPasswordConfirm) {
  if (this.refs.password.value !== this.refs.passwordConfirm.value) {
    this.refs.passwordConfirm.setCustomValidity('Passwords do not match');
  } else {
    this.refs.passwordConfirm.setCustomValidity('');
  }
}
        
if (!validity.valid) {
  if (validity.valueMissing) {
    error.textContent = `${label} is a required field`; 
  } else if (validity.typeMismatch) {
    error.textContent = `${label} should be a valid email address`; 
  } else if (isPassword && validity.patternMismatch) {
    error.textContent = `${label} should be longer than 4 chars`; 
  } else if (isPasswordConfirm && validity.customError) {
    error.textContent = 'Passwords do not match';
  }
  return false;
}

The second approach relies on react-redux-form, a third-party library that integrates form data from React components into a Redux store. This approach has a steeper learning curve than the previous one and relies less on web standards (in the W3C sense) than on conventional practices within the React community. That said, it bears some similarities to the Angular form model, such as pristine, touched, and valid properties on a $form object and on each field object. It also allows us to set validators and error messages within JSX markup, which is reminiscent of Angular directives.

<div className="form-group">
  <label>Username</label>
  <Control.text 
    model=".username"
    component={MyTextInput}
    validators={{
      required: val => val && val.length,
      validEmail: validator.isEmail
    }}
  />
  <Errors
    className="errors"
    model="user.username"
    show={showErrors}
    messages={{
      required: 'Username is required',
      validEmail: 'Username should be a valid email address',
    }}
  />
</div>

If one is already comfortable with Redux concepts, like actions and reducers, there is only the matter of getting comfortable with the specific react-redux-form API. Admittedly, this was not as easy to use as I had hoped, based on its clean and elegant documentation. Once I properly connected my UserForm component to my Redux store, I was able to listen for changes to data state (e.g. on the rrf/setSubmitFailed action) in my lower-level components (e.g. MyTextInput) and thereby achieve more control over changes in the view state. As a bonus, I added the redux-logger, so that I’m able to view all changes to data state in the browser console, including the form model that’s now part of my Redux store.

 

ReactJS Form Validation: Redux Logger Console

 

Both of these approaches are completely valid (no pun intended!) and each has its own strengths and weaknesses. In summary, the first approach benefits from its simple reliance on the “constraint validation” API, which is already a standard in modern browsers. Web standards like these have much slower churn rates, relative to trendy frameworks and libraries, which can translate into reduced fatigue for us developers. While that API was sufficient for the purposes of this demo, it may prove to be limited for some projects, such as those that require support for legacy browsers. Meanwhile, the second approach benefits from having leveraged the powers of Redux and Angular-like properties, but also probably isn’t worth the trouble of setting up on smaller projects, where Redux alone would seem like overkill. Even if an application is already configured for Redux, a form model arguably belongs where forms live – in a view component – and not in a data store at the level of the entire application.

Posted in Application Development
Share this

Jason Malfatto

Jason Malfatto has been doing professional web development for nearly 20 years with backgrounds in graphic design, computer information systems, and human-computer interaction. While he enjoys coding, he also likes to stay close to the front end, where design and development meet.

Related Posts

  • Ext JS to React: Form Submission
    Ext JS to React: Form Submission

    This is part of the Ext JS to React blog series. You can review the…

  • Ext JS to React: Form Validations
    Ext JS to React: Form Validations

    This is part of the Ext JS to React blog series. You can review the…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR