Next.js Active Links
Next.js version of activeClassName
support.
// ActiveLink.js
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);
// Header.jsx
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>
);
- Awesome, thanks a lot! Here is my more minimal version for when the className does not have to be set using a prop:
import Link from "next/link";
import { withRouter } from "next/router";
import { Children } from "react";
const ActiveLink = withRouter(({ router, children, ...props }) => (
<Link {...props}>
{React.cloneElement(Children.only(children), {
className: router.pathname === props.href ? `active` : null
})}
</Link>
));
- In case someone only needs to match the first url segment, try this:
const ActiveLink = withRouter(({ router, children, ...props }) => (
<Link {...props}>
{React.cloneElement(Children.only(children), {
className:
`/${router.pathname.split("/")[1]}` === props.href ? `active` : null
})}
</Link>
));
- Thanks a lot! Cleaned version of ActiveLink.
import { withRouter } from 'next/router';
import cx from 'classnames';
import Link from 'next/link';
import React, { Children } from 'react';
const ActiveLink = ({
router,
children,
href,
activeClassName,
...otherProps
}) => {
const child = Children.only(children);
const className = cx(child.props.className, {
[activeClassName]: router.pathname === href && activeClassName
});
return (
<Link href={href} {...otherProps}>
{React.cloneElement(child, { className })}
</Link>
);
};
- Here's a version using a Hook instead of a HOC.
import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";
const ActiveLink = ({ children, ...props }) => {
const router = useRouter();
const child = React.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>;
};
- Here's a version using a Hook and styled-components:
const ActiveLink = ({ children, ...props }) => {
const router = useRouter()
const child = React.Children.only(children)
return (
<Link {...props}>
{React.cloneElement(child, { active: router.pathname === props.href })}
</Link>
)
}
// Using example:
<ActiveLink href="/path" prefetch>
<NavLink>Vikup auto</NavLink>
</ActiveLink>
// NavLink components something like this:
const NavLink = styled.a`
color: ${({ active }) => active ? 'red' : 'black'};
`
- A version with ES6 destructuring, proptypes and useRotuer hook:
import React from 'react';
import Link from 'next/link';
import PropTypes from 'prop-types';
import { useRouter } from 'next/router';
const ActiveLink = ({ href, activeClassName, children }) => {
const router = useRouter();
const child = React.Children.only(children);
let className = child.props.className || '';
if (router.pathname === href && activeClassName) {
className = `${className} ${activeClassName}`.trim();
}
return <Link href={href}>{React.cloneElement(child, { className })}</Link>;
};
ActiveLink.propTypes = {
href: PropTypes.string,
activeClassName: PropTypes.string,
children: PropTypes.node.isRequired
};
ActiveLink.defaultProps = {
href: '',
activeClassName: ''
};
export default ActiveLink;
- I do it like this:
const ActiveLink: NextPage<Props> = ({ children, href }) => {
const router = useRouter();
return (
<Link href={href} passHref>
<Anchor active={router.pathname === href}>{children}</Anchor>
</Link>
);
};
const Anchor = styled.a<{ active: boolean }>`
color: ${props => (props.active ? 'red' : 'blue')};
`;
// If full control is needed just replace Link all together, from their docs:
import { useRouter } from 'next/router'
function ActiveLink({ children, href }) {
const router = useRouter()
const style = {
marginRight: 10,
color: router.pathname === href ? 'red' : 'black',
}
const handleClick = e => {
e.preventDefault()
router.push(href)
}
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
)
}
- If you need support for dynamic routes ([slug], [id], [...param], [[...slug]], etc.) in Next.js. The following will check if the href contains a dynamic route and then check if the "router.asPath" is equal to the "props.as". When matched the "activeClassName" will be added to the link.
import { useRouter } from "next/router";
import Link from "next/link";
import React, { Children } from "react";
const ActiveLink = ({ children, ...props }) => {
const router = useRouter();
const child = Children.only(children);
let className = child.props.className || "";
const isDynamicRoute = props.href.match(/^\/?\[{1,2}\.{0,3}[a-z]+\]{1,2}$/);
if (
router.pathname === props.href &&
!isDynamicRoute &&
props.activeClassName
) {
className = `${className} ${props.activeClassName}`.trim();
} else if (router.asPath === props.as && isDynamicRoute) {
className = `${className} ${props.activeClassName}`.trim();
}
delete props.activeClassName;
return <Link {...props}>{React.cloneElement(child, { className })}</Link>;
};
export default ActiveLink;
- A version with ES6 destructuring, proptypes and useRotuer hook:
import React from 'react';
import Link from 'next/link';
import PropTypes from 'prop-types';
import { useRouter } from 'next/router';
const ActiveLink = ({ href, activeClassName, children }) => {
const router = useRouter();
const child = React.Children.only(children);
let className = child.props.className || '';
if (router.pathname === href && activeClassName) {
className = `${className} ${activeClassName}`.trim();
}
return <Link href={href}>{React.cloneElement(child, { className })}</Link>;
};
ActiveLink.propTypes = {
href: PropTypes.string,
activeClassName: PropTypes.string,
children: PropTypes.node.isRequired
};
ActiveLink.defaultProps = {
href: '',
activeClassName: ''
};
export default ActiveLink;
Thanks a lot, for use:
<NavLink href = "/path" activeClassName = "--active">
<a className="classes">
Home Page
</a>
</NavLink>
- Found on SO :
import Link from "next/link";
import { useRouter } from "next/router";
export const MyNav = () => {
const router = useRouter();
return (
<ul>
<li className={router.pathname == "/" ? "active" : ""}>
<Link href="/">home</Link>
</li>
<li className={router.pathname == "/about" ? "active" : ""}>
<Link href="/about">about</Link>
</li>
</ul>
);
};
my code :
<Link href="/">
<a className={router.pathname ==="/" ? styles.navlinkActive : styles.navlink}>
Accueil
</a>
</Link>
Backlinks
Knowledge Base
- [[nextjs-active-link]]