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 Material UI which you can use as a starting point.
demo: https://material-pwa.vercel.app/
repository: https://github.com/ivandotv/nextjs-material-pwa
Link component is a magic component of next.js framework, that does the routing both client-side and server-side (properly rendering links for SEO purposes).
Material UI is a most popular framework for implementing Google's material design system.
Since both frameworks expect a certain HTML structure to be present in order to render their components, using them together is not straightforward as it seems, luckily it's not that hard at all.
In this article, I'm going to show you how to properly render material UI buttons and menu items as nextjs links.
Rendering a material UI button
is very easy, just wrap the button component with the nextjs link component and make sure you use passHref
property on the link component.
<Link href="/about" passHref>
<Button variant="contained" color="secondary">About</Button>
</Link>
passHref
must be used every time you use a custom component inside the Link
component. Without it when you test your code client side it will appear that everything is working because the Link
component will properly route the links however the generated a
tag will not have a href
property, so website crawlers will see no links, which will negatively impact your SEO.
The reason it appears that it works is that the link
component has a value for href
and it just listens to click events on its child components and then does the routing.
You can confirm this by disabling javascript in the browser and trying to navigate nextjs app, you will see that the links won't work.
Using ListItem Component
ListItem
component is used inside all kinds of material UI menus, and it is a little bit trickier to set up because the generated HTML structure of the component is more complex than a simple button component.
Outhere on the internet you will find a lot of solutions that are using higher-order components and passing props all over the place however, the solution is very simple and it is offered by the API of the ListItem
component itself.
What we need to do is to change the underlying element of the ListItem
component to be an a
tag. We can do this by using the component
property of the ListItem
.
<Link href="/about" passHref>
<ListItem button component="a" onClick={onClick}>
<ListItemText>About</ListItemText>
</ListItem>
</Link>
And that's it, links will work on the client-side, and they will be properly rendered on the server-side.
Top comments (15)
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....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.Sure. I found this working for me and wanted to share it with anyone who'd think it's useful. In
link.js
:Then you can just use it as a utility components just like you would with Next and MUI's buttons:
super!!!
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?!
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?
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.
Great, glad it worked out!
This is also a valid option now. Works like a charm!
Thanks Raj! Good to know!
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: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.<Link>
is taking you (because the text isn't in the<Link>
, but instead in the<Button>
), so you can fix that by using [thearia-labelledBy
attribute]((developer.mozilla.org/en-US/docs/W...) (this is recommended overaria-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:
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.
I would gladly update the post, can you give me a working gist that I can add to the article?
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
You are life saver, glad to see it in one page. This is how they should write docs! Thank you!
Very useful piece of information. Thanks.