Plaid Inspired Inputs with React Hooks and Styled Components

undraw svg category

Having been a React.js user since the days of React.createClass, I wasn’t initially motivated to dive into React Hooks when they were announced. Since then, I’ve bit the bullet and relearned the React state paradigm. Though I still occasionally dip into classes, for the most part I’m a hooks convert.

I’d like to briefly show their utility here and demonstrate a use-case where hooks have a clear advantage.

In light of Visa recently acquiring fintech startup Plaid, we’re going to hop on the bandwagon and recreate an element of their UI that I enjoy. Here’s what we’re going to build:

A quick look at the Plaid login page

Head to their site to poke around their login page for a minute. Notice the lovely animation baked into their email and password input components? I want that.

Well, I want a derivative of that. And I want to share it with you.

Let’s dive in.

Setup

If you’re not familiar with React, this post might be a bit beyond you. Follow along and feel free to post a comment if you’re lost. We’ll need a simple react app to get started.

Let it build and start up your application. We’ll add single dependency - styled components - which has become my default styling library for React apps.

We’re only going to edit the App.js file and a new Input file that we’ll create here.

Now it’s time to write some code.

A Generic Input Component

First things first, let’s build out our Input component. Here’s the skeleton we’ll start out with.

A few things going on here. Let’s break it down in pieces.

Styling

We’re encompassing the component in a single styled <div>. This InputContainer element will control the presentation of both the label and the input. We’ve specified the parent div to have position: relative and the label to have position: absolute. Doing so makes it easy to manipulate the location of label depending on how the user interacts with the input.

We’re also not including a placeholder. The label will serve as the placeholder until the user focuses on the input. This won’t quite mimic the Plaid inputs, but that’s ok. We’ll get to that part shortly, anyway.

Also, feel free to play with the colors we’re using here, they’re not essential.

Component Structure

One core React-centric architectural decision to discuss is letting the parent element control the input’s label, value, and type. By doing so, we’ve made the input more reusable, but still have standardized behavior across our application. The flexibility of this decision will make our lives easier in the long run, though it will require some work up front.

Right now we’re extracting the <label> rendering into its own function. At the moment, it might seem silly. However the next section will make this step more apparent.

Behavior

Because we want our parent component to be able to hook into the Input element’s internals, we’ve opted to take in onChange, onFocus, onBlur, and setRef function props. Do with those as you will.

And we’re left with an operational, albeit bland and not quite functional, custom input.

Improving Our UX

Try typing into our input. Not very pretty, is it? The label overlays the input text and we can’t really see what’s going on. Let’s fix that.

Finally! React hooks! If you’re underwhelmed, that’s ok. That’s really all there is to hooks. Instead of declaring this.state in a class-based component, we specify our state with React.useState(defaultValue) and destructure our state/stateSetter from the returned array.

The general pattern is const [state, setState] = React.useState(false). Easy enough, right?

If you want to learn more about hooks, check out the docs - they’re fantastic.

In our case, we’re using a focused flag to determine whether or not the user has clicked on the input. Besides that, the main refactor we’ve done here is to wrap our onFocus and onBlur handlers so we can update the <label /> and <InputContainer> components.

Notice how we’re passing the <InputContainer> the focused prop? We’ll use that to adjust the styles of our label accordingly. This is where styled components shine.

We’re interpolating a function into our template string that gives us access to all props supplied to the <InputContainer> element. Because we’re conditionally passing the focused prop depending on whether the user has clicked into the input, we have complete control over the style of the label depending on the user’s actions.

So here’s a few examples of what we end up with.

FORM STATE:
{
  "email": "",
  "password": "",
  "username": "",
  "birthday": ""
}

And voila! Look what we have here!

That’s a nice little animation. Typing into the input element triggers our focused flag and transforms the position of the label. We add a white background and a bit of padding to the left and right of the label so that it appears to break the border of our container element.

If we delete any text we’ve entered into the input, our label should return to its position as a fake placeholder. The only element that doesn’t follow this pattern has prop type="date". Because datepicker inputs have default text, we’ve auto-specified the isFocused flag as true to accommodate date inputs.

We’re creating this simple form with the following code:

Time to add the finishing touches!

Validation and Error Handling

Head back to our Plaid example. Notice how once the input element is touched and then left, an error message elegantly appears with instructions on how to remedy the issue?

We’ll recreate that functionality again using React hooks. That’s why you’re here anyway, right?

Update your Input element like so:

Now we’re getting closer. The main refactor we’ve done here is to wrap our onChange handler so that we can run an additional validation function on the input’s value.

As stated in the comments, feel free to pass in a validation function as an additional prop that can be run along with the default type-specific validations.

Doing so allows us to check for improper values and set our error state to be conditionally rendered instead of our label. We’re also passing our error to the <InputContainer> element, so that we can change border colors accordingly.

Let’s do that.

And we’re left with a much more polished version:

This is not a perfect replica of Plaid’s inputs, but it’ll do for now.

There are some improvements that could be added as well, but we’ll leave that as an exercise for the reader. Here’s some additional items that most of us had to implement at one point or another:

Resources

Tags:

Previous Post undraw svg category

Advent of Code - Day 2

Next Post undraw svg category

Just Enough Docker to Get By