How to use Next.js Links with Material-UI Button and ListElement

Both Next.js and Google's Material UI as a component library are hugely popular in the react community. However, when used in conjunction there are some pitfalls with the Links inside the application are handled. Let us have a brief look at how to properly handle Button and ListElement components, acting as internal links in the application.

Both Next.js as a Framework and Google's Material UI as a component library are hugely popular in the react community. However, when used in conjunction there are some pitfalls with the Links inside the application are handled.

Let's have a brief look at how to properly handle Button and ListElement components, acting as internal links in the application.

Button Link

The proper combination of Next.js's Link and Material UI's Button looks as follows:

import Link from 'next/link'
import { Button } from '@material-ui/core'
export default () => (
  <Link href="/home" passHref>
    <Button component="a">Home</Button>
  </Link>
)

ListElement Link

import Link from 'next/link'
import { List, ListItem, ListItemText } from '@material-ui/core'
// ...
export default () => (
  <List>
    <Link href="/home" passHref>
      <ListItem button component="a">
        <ListItemText primary="Home" />
      </ListItem>
    </Link>
  </List>
)

Explanation - passHref and component="a"

passHref: This property needs to be set on Link, because otherwise our Button will not have an href attribute set. Normally, when the child of a Link component is an <a> tag, this happens automatically. However, with a Button or ListItem component we need to force it to pass the href down.

If we fail to add this attribute, client-side routing will still work via JavaScript, but the produced HTML will be incorrect and clients not running JS will not be able to follow any links.

In the case of a Button omitting passHref would produce this HTML:

<a
  class="MuiButtonBase-root MuiButton-root MuiButton-text"
  tabindex="0"
  aria-disabled="false"
  role="button"
  ><span class="MuiButton-label">Home</span
  ><span class="MuiTouchRipple-root"></span
></a>

However, with passHref:

<a
  class="MuiButtonBase-root MuiButton-root MuiButton-text"
  tabindex="0"
  aria-disabled="false"
  href="/projects"
  ><span class="MuiButton-label">Home</span
  ><span class="MuiTouchRipple-root"></span
></a>

component="a": While a Button might automatically change to an a element when passed the href property, a <ListItem button> will not. It is therefore necessary to set the HTML element to be used by the component explicitly. A failure to do so will result in a div element with href attribute. And while this will work with JavaScript enabled, we will run into problems when clients without JS want to use these links. Furthermore, using the wrong element for a Link is problematic for screen readers.

Example rendered HTML of a <ListItem button> Link without component="a":

<div
  class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
  tabindex="0"
  aria-disabled="false"
  href="/home"
>
  <div class="MuiListItemText-root">
    <span
      class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
      >Home</span
    >
  </div>
  <span class="MuiTouchRipple-root"></span>
</div>