DEV Community

Using Next.js Link Component with Material UI Buttons and Menu Items

Ivan V. on August 05, 2020

Update This blog post refers to Material-UI V4 but it should work with the V5 also. I've created a template repository for using Next.js with Ma...
Collapse
 
codingjlu profile image
codingjlu

Okay... thanks for the article. Just want to point out though, that putting a button inside an <a> tag is invalid syntax. See stackoverflow.com/questions/639382....

Collapse
 
ivandotv profile image
Ivan V.

You can change the material ui button component to be represented with a different html element, so button could be represented with a div element.

Collapse
 
codingjlu profile image
codingjlu

Sure. I found this working for me and wanted to share it with anyone who'd think it's useful. In link.js:

import NextLink from "next/link";
import MuiLink from "@mui/material/Link";
import MuiButton from "@mui/material/Button";

export default function Link({ type, href, children, ...props }) {
  if (type === "link" || !type) {
    return (
      <NextLink href={href} passHref>
        <MuiLink {...props}>{children}</MuiLink>
      </NextLink>
    );
  } else if (type === "button") {
    return (
      <NextLink href={href} passHref>
        <MuiButton {...props}>{children}</MuiButton>
      </NextLink>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Then you can just use it as a utility components just like you would with Next and MUI's buttons:

import Link from "../path/to/link";
// Later...
<Link type="button" href="/somewhere">Yay!</Link>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
cristianeto profile image
Cristian Guamán

super!!!

Thread Thread
 
petroswursta profile image
petros-wursta

I tried this but was not able to get it to work.

I added a links.js file and added your code.

then this
import Link from "../path/to/link";
// Later...
Yay!

but it doesn't work, I get the following error.

Any help please?!

Image description

Thread Thread
 
codingjlu profile image
codingjlu

Could you show a bit more code? Did you adjust the code to actually fit your project? Did you paste the correct code into your link component?

Thread Thread
 
petroswursta profile image
petros-wursta

Thank you! Looks like I got it to work. I needed to install the mui library.

I just need to add styling b/c it just shows up as texts instead of a button.

Thread Thread
 
codingjlu profile image
codingjlu

Great, glad it worked out!

Collapse
 
rajnoua profile image
Raj • Edited

This is also a valid option now. Works like a charm!

<Button
    variant="contained"
    color="primary"
    LinkComponent={Link}
    href="/parties/add"
>
    Add Party
</Button>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ivandotv profile image
Ivan V.

Thanks Raj! Good to know!

Collapse
 
henettaja profile image
Henri Väisänen • Edited

This is the only way out of the one's mentioned on this post (and in the comments) that seem to handle keyboard navigation and other accessibility (a11y) concerns correctly without further setup. However, this does apparently require Next.js 13 to work correctly (or perhaps at all?), since previously the Next.js Link component didn't expose the native HTML <a> anchor tag.

The issue with the other implementations is that wrapping the <Link> component around the <Button> causes there to be two focusable elements for each navigation button, which isn't good for anyone navigating the page with a keyboard and likely causes additional more severe issues for people using a screen reader. I'm not sure if there are better implementations for Next.js versions prior to version 13, but I would hope there are.

If not and/or you're about to use the <Link> component wrapped around the <Button> component, then a quick and dirty fix would be the following:

  1. Make sure the inner button isn't focusable by setting tabindex="-1" on it. The reason this is a quick and dirty fix is that it means you won't get the Button's focus state when navigating to the <Link> with a keyboard.
  2. This probably messes up with a screen readers ability to read the text that communicates where the <Link> is taking you (because the text isn't in the <Link>, but instead in the <Button>), so you can fix that by using [the aria-labelledBy attribute]((developer.mozilla.org/en-US/docs/W...) (this is recommended over aria-label whenever a descriptive element is visible on the page, like in this case.

With good conscience I can't necessarily recommend this workaround even if you are not on version 13 or higher yet, because I'm not familiar with earlier Next.js versions and if there are other implementations that could potentially resolve these issues without having to manually configure the a11y stuff. From version 13 onwards that kind of implementation is @rajnoua 's example above, it overcomes all these issues altogether (you can also implement it globally via MUI theme, details in the second last paragraph).

If you're on a version lower than 13, I encourage you to try to find a better way that takes a11y into account and if you find one that's better that this quick and dirty one, please comment here for others to see as well, since this post seems to come up quite high on Google and not everyone will be able to switch to version 13 very soon.

The quick and dirty (not recommended) way to fix some of the a11y concerns of the original implementation in this post:

/* value of aria-labelledBy on the Link and the id on the corresponding 
* Button should match and they should be unique to the specific 
* Link and Button pair, take that into account if you have multiple buttons 
*/
<Link aria-labelledBy="about-nav-button" href="/about" passHref>
  <Button tabIndex={-1} id="about-nav-button">About</Button>
</Link>
Enter fullscreen mode Exit fullscreen mode

So, use @rajnoua 's example that's in the comment I'm answering to, you can even implement it globally via MUI's theme (haven't tested personally). Just check this answer by Shikyo on Stackoverflow.

Maybe author @ivandotv could update the post with info about the new implementation for Next.js 13 and with info about the a11y concerns of the original, since these comments won't reach everyone reading this.

Collapse
 
ivandotv profile image
Ivan V.

I would gladly update the post, can you give me a working gist that I can add to the article?

Thread Thread
 
henettaja profile image
Henri Väisänen

Thank you for your willingness to update the post! Sorry it took me a while to answer you, been a little busy.

Here's a quick gist I made for you about the accessible implementation for Next.js 13:

AccessibleLink.tsx gist on Github

Collapse
 
12dimov profile image
Vania Dimova

You are life saver, glad to see it in one page. This is how they should write docs! Thank you!

Collapse
 
fabarea profile image
Fabien Udriot

Very useful piece of information. Thanks.