You now know about the differences between the controlled vs. uncontrolled form inputs.

You know that controlled inputs are way more powerful and allow to provide a better user experience.

So you made yourself a couple of forms, and it occurred to you:

I know the email and password fields can’t be empty… Wouldn’t it be cool if I could disable the Submit button until the user fills both in?

Indeed it would! And it is a great UI pattern because it allows us to communicate to the user that something isn’t right before they even hit the button.

And with controlled inputs to make it a reality, there isn’t much we need!

Prerequisite: controlled inputs

Having immediate access to fields’ values is crucial for our ability to disable the button. Therefore, the requirement for this recipe is a form with controlled inputs.

The Case: simple sign-up form

Suppose we have a sign-up form with just two fields, email and password:

Our goal is to disable the button unless both email and password have something in them.

Instant validation

Recall that with the controlled inputs approach, we store all the input values in the state.

constructor () { super (); this . state = { email : '' , password : '' , }; } handleEmailChange = ( evt ) => this . setState ({ email : evt . target . value }); handlePasswordChange = ( evt ) => this . setState ({ password : evt . target . value });

This comes in especially handy for a task like ours because:

We always have the most current values of the inputs. We can react to any value change immediately.

Typing anything in the input means we end up calling setState with the new value. Or, in other words, that we are re-rendering our form on every key press.

It means we can evaluate a particular condition on every render and do something based on it.

Let’s define the condition for when the button should be disabled: if either email or password are empty. Or, alternative, the button should be enabled if both email and password are non-empty.

In code, we could express it like this, right inside render :

const { email , password } = this . state ; const isEnabled = email . length > 0 && password . length > 0 ;

Then, we can simply use this value to pass a disabled prop to the button:

< button disabled = { ! isEnabled } > Sign up < /button >

Voila, the button will now be in a disabled state unless both inputs have something typed in.

That’s amazingly simple, thanks to controlled inputs.

One more thing

Just when you think you’re done… a caveat pops up!

A form can be submitted by pressing Enter in any text input. Try that right now in the previous CodeSandbox!

We can change the handleSubmit function to check whether the fields are filled:

If they are, handleSubmit will proceed with its usual course of execution: make a request to the server for example.

will proceed with its usual course of execution: make a request to the server for example. And if they are empty, we should just return from the handler.

To avoid repeating the same piece of logic in both handleSubmit and render , let us also extract it into a function of its own.

The changes can be summarized as the following:

canBeSubmitted () { const { email , password } = this . state ; return ( email . length > 0 && password . length > 0 ); } handleSubmit = ( evt ) => { if ( ! this . canBeSubmitted ()) { evt . preventDefault (); return ; } // actual submit logic... }; render () { const isEnabled = this . canBeSubmitted (); return ( < form onSubmit= { this . handleSubmit } > ... < button disabled= { ! isEnabled } > Sign up </ button > ... </ form > ); }

You can even see it live:

Gorgeous, right? And really simple!

Going from here

What we’ve got is looking pretty good as is. However, there are opportunities to make it even better.

You can do more complex checks!

You can tweak the canBeSubmitted function to make other assertions. For example, you make sure that the password is longer than six characters, and that email contains a @ .

UX

Right now, there also is no indication of which field is the culprit that won’t let the user submit the form. A better UX would be to highlight these inputs in red.

That, however, is an entirely different story for another post… and I can send you an email when a new post on forms comes out if sign up below.