react

Building a simple login form with Material UI and React Hook Form

WK

William Kurniawan

Jul 24, 2020

In this article, I will give a short step-by-step tutorial about how to build a simple login form with Material UI and React Hook Form – a performant, flexible and extensible form library with easy-to-use validation for React applications.

Prerequisites

This tutorial will use Next.js as the application framework. I will assume that you already read my previous article about using Material UI in Next.js because we will use it as a starting point. Still, you should be able to follow along if you already have a basic understanding of React.

Install dependencies

Install react-hook-form in your project directory:
yarn add -E react-hook-form

Get started

In your pages directory, create a new file called login.tsx. And, write the following code:
import React from 'react';
// Modules
import { NextPage } from 'next/types';

const LoginPage: NextPage = () => {
  return <div>Login Page</div>;
};

export default LoginPage;
Then, save your file and run the application in the development server by running the following command in your terminal:
yarn dev
By executing the command above, your application will run on localhost:3000. You can try to visit localhost:3000/login and you will see a login page.

Build the user interface

At the moment, we have a simple login page which only has "Login Page" text on it. Let's start writing some codes to build the user interface. Open login.tsx and import these modules:
import Button from '@material-ui/core/Button';
import Container from '@material-ui/core/Container';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';
By default, Material UI uses JSS-based styling to style your application. However, they allow you to use other styling solutions, such as Plain CSS, Global CSS, Styled Components, CSS Modules, and Emotion. You can learn more about each styling solution from their official documentation, but, in this tutorial, I will use the default styling solution provided by Material UI to keep it simple. Open login.tsx and write the following code:
// ... imports

const useStyles = makeStyles((theme) => ({
  container: {
    padding: theme.spacing(3),
  },
});

const LoginPage: NextPage = () => {
  const classes = useStyles();

  return (
    <Container className={classes.container} maxWidth="xs">
      <div>Login Page</div>
    </Container>
  );
};

export default LoginPage;
Then, save your file, and you will see the changes you just made in the browser. Thanks to Next.js for enabling Fast Refresh which helps us to see changes reflected directly to the browser without having to re-run the development server.
In this tutorial, we will use a basic user interface to build our login form. However, feel free to come up with other design ideas to build your login form. Without wasting more time, let's start to build our login form. Write the following code in login.tsx:
// ... rest of your codes

const LoginPage: NextPage = () => {
  const classes = useStyles();

  return (
    <Container className={classes.container} maxWidth="xs">
      <form>
        <Grid container spacing={3}>
          <Grid item xs={12}>
            <Grid container spacing={2}>
              <Grid item xs={12}>
                <TextField fullWidth label="Email" name="email" size="small" variant="outlined" />
              </Grid>
              <Grid item xs={12}>
                <TextField
                  fullWidth
                  label="Password"
                  name="password"
                  size="small"
                  type="password"
                  variant="outlined"
                />
              </Grid>
            </Grid>
          </Grid>
          <Grid item xs={12}>
            <Button color="secondary" fullWidth type="submit" variant="contained">
              Log in
            </Button>
          </Grid>
        </Grid>
      </form>
    </Container>
  );
};

// ... rest of your codes
Then, save your file, and you will see the changes you just made in the browser.
Now, we finish building the user interface of our login form. By the end of this section, your code should look like this:
import React from 'react';
// Modules
import { NextPage } from 'next/types';
// MUI Core
import Button from '@material-ui/core/Button';
import Container from '@material-ui/core/Container';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
  container: {
    padding: theme.spacing(3),
  },
}));

const LoginPage: NextPage = () => {
  const classes = useStyles();

  return (
    <Container className={classes.container} maxWidth="xs">
      <form>
        <Grid container spacing={3}>
          <Grid item xs={12}>
            <Grid container spacing={2}>
              <Grid item xs={12}>
                <TextField fullWidth label="Email" name="email" size="small" variant="outlined" />
              </Grid>
              <Grid item xs={12}>
                <TextField
                  fullWidth
                  label="Password"
                  name="password"
                  size="small"
                  type="password"
                  variant="outlined"
                />
              </Grid>
            </Grid>
          </Grid>
          <Grid item xs={12}>
            <Button color="secondary" fullWidth type="submit" variant="contained">
              Log in
            </Button>
          </Grid>
        </Grid>
      </form>
    </Container>
  );
};

export default LoginPage;

Implementing React Hook Form

In this section, we are going to implement react hook form to handle the login functionality. To use React Hook Form, we need to import react-hook-form. Write the following code in your login.tsx:
import { useForm } from 'react-hook-form';
Since we are using TypeScript, we need to define an interface to make our form strongly typed. Write the following code in your login.tsx:
// ... rest of your codes

interface FormData {
  email: string;
  password: string;
}

const useStyles = makeStyles((theme) => ({
  container: {
    padding: theme.spacing(3),
  },
}));

// ... rest of your codes
Then, write the following code to implement react-hook-form in our component:
// ... rest of your codes

const LoginPage: NextPage = () => {
  const { handleSubmit, register } = useForm<FormData>();
  
  const onSubmit = handleSubmit((data) => {
    console.log(data);
  });
  
  // ... rest of your codes
};

// ... rest of your codes
One of the key concepts in React Hook Form is to register your uncontrolled component into the hook. This will make its value available for both the form validation and submission. Each field is required to have a unique name as a key for the registration process. Write the following code to register our text fields into the hook:
<Grid item xs={12}>
  <TextField
    fullWidth
    inputRef={register}
    label="Email"
    name="email"
    size="small"
    variant="outlined"
  />
</Grid>
<Grid item xs={12}>
  <TextField
    fullWidth
    inputRef={register}
    label="Password"
    name="password"
    size="small"
    type="password"
    variant="outlined"
  />
</Grid>
Then, pass onSubmit into the onSubmit props on <form /> component:
<form onSubmit={onSubmit}>
  {/* ... rest of your codes */}
</form>
Then, save your file, and you will see the changes you just made in the browser. Try to input email and password, and then press the login button. You will see the email and password in the browser's console.
By the end of this section, your code will look like this:
import React from 'react';
// Modules
import { NextPage } from 'next/types';
import { useForm } from 'react-hook-form';
// MUI Core
import Button from '@material-ui/core/Button';
import Container from '@material-ui/core/Container';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';

interface FormData {
  email: string;
  password: string;
}

const useStyles = makeStyles((theme) => ({
  container: {
    padding: theme.spacing(3),
  },
}));

const LoginPage: NextPage = () => {
  const { handleSubmit, register } = useForm<FormData>();

  const classes = useStyles();

  const onSubmit = handleSubmit((data) => {
    console.log(data);
  });

  return (
    <Container className={classes.container} maxWidth="xs">
      <form onSubmit={onSubmit}>
        <Grid container spacing={3}>
          <Grid item xs={12}>
            <Grid container spacing={2}>
              <Grid item xs={12}>
                <TextField
                  fullWidth
                  inputRef={register}
                  label="Email"
                  name="email"
                  size="small"
                  variant="outlined"
                />
              </Grid>
              <Grid item xs={12}>
                <TextField
                  fullWidth
                  inputRef={register}
                  label="Password"
                  name="password"
                  size="small"
                  type="password"
                  variant="outlined"
                />
              </Grid>
            </Grid>
          </Grid>
          <Grid item xs={12}>
            <Button color="secondary" fullWidth type="submit" variant="contained">
              Log in
            </Button>
          </Grid>
        </Grid>
      </form>
    </Container>
  );
};

export default LoginPage;

Summary

Hopefully, this article has helped you to understand how to implement React Hook Form in Material UI. This article only covers basic concept of React Hook Form. If you want to learn more about React Hook Form, you can visit its official documentation here.

Copyright © 2020 William Kurniawan. All rights reserved.