ReactHustle

How to Compose Next 13 Link and MUI Link (with Typescript)

Jasser Mark Arioste

Jasser Mark Arioste

How to Compose Next 13 Link and MUI Link (with Typescript)

Hello, hustler! NextJS recently released a major update which is Next 13, It included an update to the critical next/link component.  In this tutorial, you'll learn how to customize the MUI Link component and integrate the behavior of the next/link component and fix the naming conflict of the href prop of both components.

Introduction #

NextJS and MUI are a very common combination for front-end development and it speeds up the development time by a lot. When NextJS released version 13, I found it difficult to customize the MUI Link with next/link. I was able to figure out two ways to achieve the desired behavior. 

The techniques shown here can be used not only for the next/link component but when creating any other custom components as well. 

Scope of this Tutorial #

We limit this tutorial to the following versions:

  • MUI version 5.x and up
  • NextJS version 13.x and up

Way #1 - Using the component Property of MuiLink #

If you don't need to create a custom component you can directly use the component property of MuiLink, but it's not without its drawbacks. For example:

import { Link as MuiLink } from "@mui/material";
import NextLink from "next/link";
const Container = () => {
  return (
    <div>
      {/* This will use "prop forwarding" and MuiLink will automatically have the props of NextLink */}
      <MuiLink component={NextLink} prefetch={false} href={"/page1"}>
        hello
      </MuiLink>
      {/* However, passing a URLObject in href results in a type error */}
      <MuiLink
        component={NextLink}
        prefetch={false}
        href={{ pathname: "/page1" }} //type error
      >
        hello
      </MuiLink>
    </div>
  );
};
export default Container;

123456789101112131415161718192021

This pattern will use prop forwarding and MuiLink will automatically get the NextLink props.  But since both MuiLink and NextLink have href props, this causes name collisions and results in a type error. It will use the href prop from MuiLink.

Way #2 - Modifying NextLink href Property #

To solve the problem above, you can extend the NextLink component and rename the href prop to something else to avoid naming collisions. Here's how to do it in Typescript.

// components/MyLink.tsx
import { LinkProps, Link as MuiLink } from "@mui/material";
import NextLink, { LinkProps as NextLinkProps } from "next/link";
// Defining the CustomNextLink
export type CustomNextLinkProps = Omit<NextLinkProps, "href"> & {
  _href: NextLinkProps["href"];
};
export const CustomNextLink = ({ _href, ...props }: CustomNextLinkProps) => {
  return <NextLink href={_href} {...props} />;
};
// combine MUI LinkProps with NextLinkProps
type CombinedLinkProps = LinkProps<typeof NextLink>;
// remove both href properties
// and define a new href property using NextLinkProps
type MyLinkProps = Omit<CombinedLinkProps, "href"> & {
  href: NextLinkProps["href"];
};
const MyLink = ({ href, ...props }: MyLinkProps) => {
  // use _href props of CustomNextLink to set the href
  return <MuiLink {...props} component={CustomNextLink} _href={href} />;
};
export default MyLink;
12345678910111213141516171819202122

Explanation:
Line 5-10: We define the component CustomNextLink that uses _href prop to avoid naming collisions. This is an internal component for this file and is not to be used anywhere else.

Line 12-17: We take a roundabout way to remove the href props and define a new href prop that uses the one NextLinkProps for our custom MuiLink component.

Line 20: Here's where the magic happens. We use the _href props from our CustomNextLink and pass the next href prop that we just defined on line 16.

Perhaps there's a better way of defining the MyLinkProps, but I found this to be really easy to digest.

Usage:

import MyLink from "./MyLink";
// MyLink usage
const Container2 = () => {
  return (
    <div>
      <MyLink
        prefetch={false}
        scroll={false}
        sx={{ color: "warning.main" }}
        href={{ pathname: "/page1" }}
      >
        hello
      </MyLink>
      <MyLink
        prefetch={false}
        scroll={false}
        sx={{ color: "warning.main" }}
        href="/page2" //Both string and URLObject are accepted
      >
        hello
      </MyLink>
    </div>
  );
};
export default Container2;
12345678910111213141516171819202122232425

Here we can see that it's a very seamless integration with NextLink and MUILink. I think this is a very good pattern to know.

Usage with styled:

If you want to use styled components, you can use the new wrapper component as the base:

// components/StyledLink:
import { styled } from "@mui/material/styles";
import MyLink, { MyLinkProps } from "./MyLink";

const StyledLink = styled(MyLink)<MyLinkProps>({});
StyledLink.defaultProps = {
  color: "seagreen",
  underline: "hover",
};
export default StyledLink;
12345678910

Full Code #

The full code can be accessed at Github: Next Link with MUI Link Tutorial

Conclusion #

We learned how to integrate next/link with MUI Link component by creating a custom component and modifying the default props to avoid naming collisions. I think this is a very important pattern to learn and master, and you can apply it in other MUI components as well. 

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Credits: Image by Karl Egger from Pixabay

Share this post!

Related Posts