import Link from './Link'; // our version of link
export default () => (
<header className="Header">
<nav>
<Link activeClassName="active" href="/">
<a className="some-other-class">Home</a>
</Link>
<Link activeClassName="active" href="/about">
<a>About</a>
</Link>
<Link activeClassName="active" href="/contact">
<a>Contact</a>
</Link>
</nav>
</header>
);
Last active
November 2, 2024 23:50
-
-
Save remy/0dde38897d6d660f0b63867c2344fb59 to your computer and use it in GitHub Desktop.
Next.js version of `activeClassName` support.
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 { withRouter } from 'next/router'; | |
import Link from 'next/link'; | |
import React, { Children } from 'react'; | |
const ActiveLink = ({ router, children, ...props }) => { | |
const child = Children.only(children); | |
let className = child.props.className || ''; | |
if (router.pathname === props.href && props.activeClassName) { | |
className = `${className} ${props.activeClassName}`.trim(); | |
} | |
delete props.activeClassName; | |
return <Link {...props}>{React.cloneElement(child, { className })}</Link>; | |
}; | |
export default withRouter(ActiveLink); |
I rather have my own function that creates a pure next/link
component:
import Link from "next/link";
import {useRouter} from "next/router";
import styles from './Toolbar.module.css'
const Toolbar = () => {
const router = useRouter();
const activeLink = (path, content, activeClass = styles.active, normalClass = '') => {
let className = router.pathname === path ? activeClass : normalClass;
return <Link href={path}><a className={className}>{content}</a></Link>
}
return <div className={styles.toolbar}>
<nav>
{activeLink('/', 'Home')}
{activeLink('/about', 'About')}
</nav>
</div>
}
export default Toolbar;
If you need a more reusable function, you can pass the router:
import Link from "next/link"
const createActiveLink = (router, path, content, activeClass = 'active', normalClass = '') => {
let className = router.pathname === path ? activeClass : normalClass;
return <Link href={path}><a className={className}>{content}</a></Link>
}
export default createActiveLink
typed. 21/10/22
Thank you very much remy san XD
import { withRouter, NextRouter } from 'next/router';
import React, { ReactElement } from 'react';
import Link from 'next/link';
type Props = {
router: NextRouter;
children: ReactElement;
href: string;
activeClassName: string;
}
const ActiveLink = ({ router, children, ...props }: Props) => {
const child = children;
let className: string = child.props.className;
if (router.pathname == props.href) {
className = `${className} ${props.activeClassName}`;
}
return (
<Link {...props}>{React.cloneElement(child, { className })}</Link>
);
}
export default withRouter(ActiveLink);
Here's my version of ActiveLink
for Next.js + TypeScript:
import Link, { LinkProps } from "next/link";
import { NextRouter, useRouter } from "next/router";
import { FC, PropsWithChildren } from "react";
type ActiveLinkProps = LinkProps & {
activeClassName: string;
className: string;
};
export const ActiveLink: FC<ActiveLinkProps> = ({ children, ...props }: PropsWithChildren<ActiveLinkProps>) => {
const router: NextRouter = useRouter();
const className: string =
props.className + ((router.pathname === props.href && props.activeClassName) ? " " + props.activeClassName : "");
return (
<Link {...props}>
<a className={className}>{children}</a>
</Link>
);
};
Example of usage:
<ActiveLink href="/" className="menulink" activeClassName="active">
Home
</ActiveLink>
The difference is you no longer need <a>
tag inside.
Benefits:
- TypeScript-friendly
- You no longer need to find child element and its type with
Children.only
- You no longer need to do
React.cloneElement
- You won't forget to put
<a>
inside anymore.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
All these look like great solutions, but regrettably I cannot make any to work reliably with this navbar structure:
If I click on a link nested in a different
<Nav>
(for example, "Contact") the highlighting of the previous one isn't removed...Am I missing something obvious?