Validating with controlled forms in React: Handling inputs

Christine Zosche
4 min readApr 10, 2021

We’re all familiar with web forms — you’ve probably used one today.

While there are many ways to create web forms, one of the simplest is to write it in pure HTML. In fact, for seeming so unsophisticated, writing forms in HTML alone gives us a surprising amount of power. One of the main ones, though, is simple validation — making sure that any form won’t submit if the values entered don’t meet specific criteria. After all, no one needs bad or missing data — what use is collecting information if it weighs down your database or introduces bugs to your programs?

For example, I’ve created an app with a form to collect image URLs. Simply including “required” in the <input> tag in basic HTML prevents the form from submitting unless the input field includes a URL.

Simple and effective. But, let’s say we don’t want simple. We’re working in React, in the modern web, so we can surely improve on this functionality. We can do this using a controlled form. With controlled forms, the values entered are stored in the component’s state, every time those values change. From there, we can do many things — manipulate what the input displays, send data to other parts of our app, among others — but in the context of our case here, we can validate data. More than that, we can do it in real time, displaying to the user whether the information they’ve entered is valid as they type. I’m sure you’ve seen examples of forms like this across the internet.

To do this, we start by setting up our state and event handlers like we would in any controlled form.

In this app, our state includes an “images” object, with a key and value for each image url.

We can write our event handler to be dynamic and usable for all of our inputs. To update the corresponding image within our form’s component state when a user types into an input field, our event handler looks like this:

This isn’t different from how we would write any controlled component. When a user types in our input field, the corresponding value in the component’s state is updated, and the state is reflected in what text shows up in our input, in real time (for all practical purposes).

Now that we know what the user’s input is as they type, let’s think of how we can validate whether this input is valid. For simplicity, since we want to make sure an input is a URL, let’s say that the opening characters of the input have to include “https://” or “http://”.

We can write a simple method that can return “true” or “false” based on whether any given string meets those criteria.

Then, to validate our user’s input, we can simply pass the value stored in our component state into the method, which will return “true” or “false”. Useful, but only if we do something with that Boolean value — otherwise, how will the user know whether what they’re writing is valid? In this example, let’s manipulate how the input field looks — more specifically, let’s but a bright red border around it if the text inside is not valid.

How can we do that? Simple CSS, assigning a class name to the input field depending on whether or not the text is valid. Let’s define an “invalid” class that renders a bright red border around an input with that class name:

Now, any input with the class name “invalid” will have a red border. Now, we just have to make sure our input field has that class name when its text is invalid. We can do this with a simple ternary statement.

Within the input tag, if our “checkIfValid” method returns true, the input has a class name of “valid”. If it returns false, the input takes a class name of “invalid”, and a bright red border appears.

Then, when the input is valid, the border disappears.

This is the beauty of React. Because components update whenever the state changes, our “checkIfValid” method is called every time a user types, and the input border updates accordingly.

We’re well on our way to a fully functioning controlled form. In my next post, I’ll talk about how to handle form submissions.

--

--