I made a nice and unobtrusive animation for form validation with redux-form:

a

To implement it, I made a FormInput component:

import React, { Component } from 'react'

export default class FormInput extends Component {
  render() {

    const inputState = (field) => {
      if (this.props.field.error) {
        return 'error'
      } else if (this.props.field.touched && this.props.field.value) {
        return 'pass'
      } else {
        return ''
      }
    }

    const inputType = this.props.type || 'text'

    return (
      <div>
        <label>{this.props.label}</label>
        <input className={inputState(this.props.field)} type={inputType} {...this.props.field} />
      </div>
    )
  }
}

The CSS for the component is surprisingly simple:

input {
  border: 1px solid #ddd;
  padding-left: 5px;
  width: 300px;
  transition: 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55) all;
}

input.error {
  border-left: 8px solid #D0021B;
}

input.pass {
  border-left: 8px solid #7ED321;
}

input:focus {
  border-left: 8px solid #449CFA;
}

The border style is for overriding the browser’s default border styling, and can be left out if you already have existing border styles for your inputs. The padding-left gives a bit of spacing between the indicator and the text in the input.

It’s important to note that, for the width of the input to stay fixed even as the border is transitioned in, you need to make sure that your box-sizing is set to border-box. Paul Irish’s CSS snippet works like a charm:

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

Now we can use it in a redux-form component like so:

const validate = values => {
  const errors = {}
  if (!(values.email) || !(values.email.trim())) {
    errors.email = 'Email required'
  }
  return errors
}

@reduxForm({
  form: 'login',
  fields: ['email', 'password'],
  validate
})
class LoginForm extends Component {
  static propTypes = {
    fields: PropTypes.object,
    handleSubmit: PropTypes.func
  }
  render() {
    const { fields: {email, password}, handleSubmit } = this.props
    return (
      <form onSubmit={handleSubmit}>
        <FormInput label='Email' field={email} />
        <FormInput type="password" label='Password' field={password} />
        <button type='submit' onClick={handleSubmit}>Login</button>
      </form>
    )
  }
}

🙌