Last active
May 6, 2024 21:38
-
-
Save kachar/028b6994eb6b160e2475c1bb03e33e6a to your computer and use it in GitHub Desktop.
Next.js Link + Material UI Link/Button components bundled with forwardRef
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { LinkProps, Link as MuiLink } from '@mui/material' | |
import NextLink from 'next/link' | |
export default function Link(props: LinkProps<'a'>) { | |
return <MuiLink component={NextLink} {...props} /> | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Plain JS version + prop-types | |
// Thanks to @IvanAdmaers | |
import PropTypes from 'prop-types'; | |
import { forwardRef } from 'react'; | |
import NextLink from 'next/link'; | |
import { Link as MuiLink } from '@material-ui/core'; | |
const Link = forwardRef(({ href, as, prefetch, ...props }, ref) => { | |
return ( | |
<NextLink href={href} as={as} prefetch={prefetch} passHref> | |
<MuiLink ref={ref} {...props} /> | |
</NextLink> | |
); | |
}); | |
Link.displayName = 'Link'; | |
Link.defaultProps = { | |
href: '#', | |
prefetch: false, | |
}; | |
Link.propTypes = { | |
href: PropTypes.string, | |
as: PropTypes.string, | |
prefetch: PropTypes.bool, | |
}; | |
export default Link; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MaterialUI v4 | |
import React, { forwardRef, Ref } from 'react' | |
import Link, { LinkProps } from 'next/link' | |
import { Link as MuiLink, LinkProps as MuiLinkProps } from '@material-ui/core' | |
type LinkRef = HTMLAnchorElement | |
type NextLinkProps = Omit<MuiLinkProps, 'href' | 'classes'> & | |
Pick<LinkProps, 'href' | 'as' | 'prefetch'> | |
const NextLink = ({ href, as, prefetch, ...props }: LinkProps, ref: Ref<LinkRef>) => ( | |
<Link href={href} as={as} prefetch={prefetch} passHref> | |
<MuiLink ref={ref} {...props} /> | |
</Link> | |
) | |
export default forwardRef<LinkRef, NextLinkProps>(NextLink) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MaterialUI v5 | |
// Thanks to @bryantobing12 | |
import React from "react"; | |
import { Link as LinkMUI, LinkProps as LinkMUIProps } from "@mui/material"; | |
import NextLink, { LinkProps as NextLinkProps } from "next/link"; | |
export type LinkProps = Omit<LinkMUIProps, "href" | "classes"> & | |
Pick<NextLinkProps, "href" | "as" | "prefetch">; | |
export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>( | |
({ href, as, prefetch, ...props }, ref) => ( | |
<NextLink href={href} as={as} prefetch={prefetch} passHref> | |
<LinkMUI ref={ref} {...props} /> | |
</NextLink> | |
) | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @source https://github.com/mui/material-ui/tree/master/examples/nextjs-with-typescript | |
// Updated in this gist by @alecriarstudio | |
import * as React from 'react'; | |
import clsx from 'clsx'; | |
import { useRouter } from 'next/router'; | |
import NextLink, { LinkProps as NextLinkProps } from 'next/link'; | |
import MuiLink, { LinkProps as MuiLinkProps } from '@mui/material/Link'; | |
import { styled } from '@mui/material/styles'; | |
// Add support for the sx prop for consistency with the other branches. | |
const Anchor = styled('a')({}); | |
type NextLinkComposedProps = { | |
to: NextLinkProps["href"]; | |
linkAs?: NextLinkProps["as"]; | |
} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & | |
Omit<NextLinkProps, "href" | "as">; | |
export const NextLinkComposed = React.forwardRef<HTMLAnchorElement, NextLinkComposedProps>( | |
function NextLinkComposed(props, ref) { | |
const { to, linkAs, replace, scroll, shallow, prefetch, locale, ...other } = props; | |
return ( | |
<NextLink | |
href={to} | |
prefetch={prefetch} | |
as={linkAs} | |
replace={replace} | |
scroll={scroll} | |
shallow={shallow} | |
passHref | |
locale={locale} | |
> | |
<Anchor ref={ref} {...other} /> | |
</NextLink> | |
); | |
}, | |
); | |
export type LinkProps = { | |
activeClassName?: string; | |
as?: NextLinkProps['as']; | |
href: NextLinkProps['href']; | |
linkAs?: NextLinkProps['as']; // Useful when the as prop is shallow by styled(). | |
noLinkStyle?: boolean; | |
} & Omit<NextLinkComposedProps, 'to' | 'linkAs' | 'href'> & | |
Omit<MuiLinkProps, 'href'>; | |
// A styled version of the Next.js Link component: | |
// https://nextjs.org/docs/api-reference/next/link | |
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(function Link(props, ref) { | |
const { | |
activeClassName = 'active', | |
as, | |
className: classNameProps, | |
href, | |
linkAs: linkAsProp, | |
locale, | |
noLinkStyle, | |
prefetch, | |
replace, | |
role, // Link don't have roles. | |
scroll, | |
shallow, | |
...other | |
} = props; | |
const router = useRouter(); | |
const pathname = typeof href === 'string' ? href : href.pathname; | |
const className = clsx(classNameProps, { | |
[activeClassName]: router.pathname === pathname && activeClassName, | |
}); | |
const isExternal = | |
typeof href === 'string' && (href.indexOf('http') === 0 || href.indexOf('mailto:') === 0); | |
if (isExternal) { | |
if (noLinkStyle) { | |
return <Anchor className={className} href={href} ref={ref} {...other} />; | |
} | |
return <MuiLink className={className} href={href} ref={ref} {...other} />; | |
} | |
const linkAs = linkAsProp || as; | |
const nextjsProps = { to: href, linkAs, replace, scroll, shallow, prefetch, locale }; | |
if (noLinkStyle) { | |
return <NextLinkComposed className={className} ref={ref} {...nextjsProps} {...other} />; | |
} | |
return ( | |
<MuiLink | |
component={NextLinkComposed} | |
className={className} | |
ref={ref} | |
{...nextjsProps} | |
{...other} | |
/> | |
); | |
}); | |
export default Link; |
Thanks @kachar
Latest approach is indeed simple but we are not able to pass href as an object. It only accepts string
type. Given that NextLink accepts a URL object as href, how can we get same behaviour here?
@vighnesh153 I've tried to make it work with LinkProps['href']
but the MuiButton expectation is a hardcoded { href: string }
so I don't think it's currently possible:
// node_modules/@mui/material/Button/Button.d.ts
export type ExtendButton<M extends OverridableTypeMap> = ((
props: { href: string } & OverrideProps<ExtendButtonBaseTypeMap<M>, 'a'>,
) => JSX.Element) &
OverridableComponent<ExtendButtonBaseTypeMap<M>>;
What I end up doing in multiple projects is having a general routes.ts
file that contains all the app links as follows:
export const routes = {
post: {
byId: (id: string) => `/post/${id}`,
}
}
and use it as:
<LinkButton color="secondary" variant="contained" href={routes.post.byId(query.id)}>
View post
</LinkButton>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
With the update brought by Next13 we're able to simplify the
next/link
+ mui link/button process a lot:Read more at https://stackoverflow.com/a/74419666/668245