Using Next.js and Material UI Together

Next.js is a convenient and powerful framework for React. Its main benefit over using React directly is its transparent support for Server-Side Rendering.
Material UI is a very popular set of React components implementing Google’s Material Design guidelines.

Both libraries are impressive, but there are some tricks to know to make them playing well together.

Bootstrapping

Setting up MUI in a Next project requires some non-trivial tweaks to Next’s initialization process. Conveniently, Material UI provides a skeleton containing a working Next.js project with Material UI already properly configured. It’s the easiest way to kickstart a new project using both tools, don’t miss it!

# Download the skeleton
$ curl https://codeload.github.com/mui-org/material-ui/tar.gz/master | tar -xz --strip=2  material-ui-master/examples/nextjs
$ cd nextjs
# Install the deps
$ yarn install
# Start the project
$ yarn dev

To learn how to integrate Material UI in an existing project, take a look to pages/_document.js and pages/_app.js they contain most the wiring logic.

Forms

Material UI is especially useful because of the large set of form components it provides. But handling forms with React (and so with Next) is tedious and verbose. My colleague Morgan Auchedé recently told me about Formik. Formik is a tiny yet super powerful library allowing to easily create forms with React. And good news: it plays very well with Next! Here is how a basic login form looks when using Formik:

import React from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';

export default MyForm = () => (
    <Formik
      initialValues={{ email: '', password: '' }}
      validate={values => {
        // Your client-side validation logic
      }}
      onSubmit={(values, { setSubmitting }) => {
        // Call your API
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field type="email" name="email" />
          <ErrorMessage name="email" />
          <Field type="password" name="password" />
          <ErrorMessage name="password" />
          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
);

Nice! However, when switching to Material UI inputs, the high level helper components provided by Formik become almost useless. Our forms are verbose again. Fortunately, a small library intuitively named formik-material-ui makes it easy to bridge both libraries! Here is the same form as before (including error handling), but rendered using Material UI components:

import React from 'react';
import { Formik, Form, Field } from 'formik';
import { TextField } from 'formik-material-ui';
import Button from "@material-ui/core/Button";

export default MyForm = () => (
    <Formik
      initialValues={{ email: '', password: '' }}
      validate={values => {
        // Your client-side validation logic
      }}
      onSubmit={(values, { setSubmitting }) => {
        // Call your API
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field type="email" name="email" component="TextField" />
          <Field type="password" name="password" component="TextField" />
          <Button
           type="submit"
           fullWidth
           variant="contained"
           color="primary"
           disabled={isSubmitting}
         >
           Submit
         </Button>
        </Form>
      )}
    </Formik>
);

This form is even less verbose, and is now looking good!

Buttons and Routing

Next.js comes with a nice routing system working transparently regardless if the app is executed client-side or server-side. It’s one of the biggest strength of the framework. However, in Material UI, the Button component is often used to trigger navigation between pages, and using buttons with the Router isn’t very intuitive. Still, it’s easy to do:

import React from "react";
import Link from "next/link";
import Button from "@material-ui/core/Button";

export default MyLink = () => (
  <Link href="/pricing" passHref>
    <Button component="a">Managed version</Button>
  </Link>
);

First, we set the component prop of Button to a. It tells Material UI to use an anchor for this button, instead of a… button by default. Then, we set the passHref prop of the Link element, it hints the Router to pass the href prop to the child component, even if doesn’t look like an anchor. Actually (because of the component prop we set earlier), the grandchild will be an anchor, and Material UI will forward the href prop to it! The rendered a element now has a proper href attribute, both client-side and in the server-side generated HTML. Good SEO, for free!

The same trick can be used with the Typography component:

import React from "react";
import Link from "next/link";
import Button from "@material-ui/core/Typography";

export default MyLink = () => (
  <Link href="/pricing" passHref>
    <Typography variant="caption" component="a">Managed version</Typography>
  </Link>
);

This time, we created a link looking like a caption!

That’s all for today. Have fun with Next and Material UI! For more tricks about JavaScript (among various other technologies), follow me on Twitter!

Leave a Reply