admin管理员组文章数量:1271801
I am using Next.js and want to add an active class to a nav link when the page it links to matches the url. But I also want it to be active when the url is deeper than only the page.
For example, a nav link to /register
would be "active" for:
/register
/register/sign-up
I am using Next.js and want to add an active class to a nav link when the page it links to matches the url. But I also want it to be active when the url is deeper than only the page.
For example, a nav link to /register
would be "active" for:
/register
/register/sign-up
- What is next.js? obviously a JavaScript file, but it could literally contain anything, how is anyone supposed to help based on the little information provided in your post? – SPlatten Commented Feb 20, 2020 at 16:06
- 7 @SPlatte. nextjs. I thought it was obvious with it being so well known in the industry. – react-dev-in-training Commented Feb 20, 2020 at 16:08
- Not to anyone not familiar with react. Have you tried embedding the URI in an iframe? – SPlatten Commented Feb 20, 2020 at 16:14
- No. I think that would be overkill. I have used this and it works fine (flaviocopes./nextjs-active-link), But only for one layer. It doesn't work for deeper segmented urls @SPlatten – react-dev-in-training Commented Feb 20, 2020 at 16:32
4 Answers
Reset to default 7Here's a NavLink
ponent I wrote for Next.js. It has the same API as Link
, with the addition of two props:
- an
activeClassName
that will be applied to the link when it matches - (optional)
exact
- prevents matching “deeper” routes)
import React from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function NavLink({ href, as, exact, activeClassName, children, ...props }) {
const { asPath } = useRouter();
// Normalize and split paths into their segments
const segment = (p) => new URL(p, 'http://example.').pathname.split('/').filter(s => s);
const currentPath = segment(asPath);
const targetPath = segment(as || href);
// The route is active if all of the following are true:
// 1. There are at least as many segments in the current route as in the destination route
// 2. The current route matches the destination route
// 3. If we're in “exact" mode, there are no extra path segments at the end
const isActive = currentPath.length >= targetPath.length
&& targetPath.every((p, i) => currentPath[i] === p)
&& (!exact || targetPath.length === currentPath.length);
const child = React.Children.only(children);
const className = ((child.props.className || '') + ' ' + (isActive ? activeClassName : '')).trim();
return (
<Link href={href} as={as} {...props}>
{React.cloneElement(child, { className })}
</Link>
);
}
It's possible to make this simpler and more robust by relying on the route-matching functionality from the path-to-regexp
module, which is already a dependency of Next.js:
import React from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { pathToRegexp } from 'path-to-regexp';
export default function NavLink({ href, as, exact, activeClassName, children, ...props }) {
const { asPath } = useRouter();
const isActive = pathToRegexp(as || href, [], { sensitive: true, end: !!exact }).test(asPath);
const child = React.Children.only(children);
const className = ((child.props.className || '') + ' ' + (isActive ? activeClassName : '')).trim();
return (
<Link href={href} as={as} {...props}>
{React.cloneElement(child, { className })}
</Link>
);
}
Not sure about previous versions, but for Next.JS 14.*, it's much simpler to implement.
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function MyNavBar() {
const segment = useSelectedLayoutSegment()
return (
<div>
<Link className={segment == null ? styles.active : "")} href="/"> Home</Link>
<Link className={segment == 'info' ? styles.active : "")} href="/info">Info</Link>
</div>
)}
You can pare the segment var to the Link entry href you're on, or to null for home.
Note that this forces you to make the navBar a client ponent.
Note also that there's a way to look at more than one navigation layer. This is explained in the documentation as well.
See the documentation here: https://nextjs/docs/app/api-reference/functions/use-selected-layout-segment
I built on an example in the nextjs docs and a little bit of what's in the react-router-dom source to e up with this
import { useRouter } from "next/router";
import Link, { LinkProps } from "next/link";
import React, { useState, useEffect } from "react";
type Props = LinkProps & {
className?: string;
activeClassName: string;
children?:
| React.ReactNode
| ((props: { isActive: boolean }) => React.ReactNode);
};
export default function NavLink({
children,
activeClassName,
className,
...props
}: Props) {
const { asPath, isReady } = useRouter();
const [putedClassName, setComputedClassName] = useState(className);
// Dynamic route will be matched via props.as
// Static route will be matched via props.href
const linkPathname = new URL(
(props.as || props.href) as string,
location.href
).pathname;
// Using URL().pathname to get rid of query and hash
const activePathname = new URL(asPath, location.href).pathname;
const isActive = linkPathname === activePathname;
useEffect(() => {
// Check if the router fields are updated client-side
if (isReady) {
const newClassName = isActive
? `${className} ${activeClassName}`.trim()
: className;
if (newClassName !== putedClassName) {
setComputedClassName(newClassName);
}
}
}, [
asPath,
isReady,
props.as,
props.href,
activeClassName,
className,
putedClassName,
isActive,
]);
return (
<Link className={putedClassName} {...props}>
{typeof children === "function" ? children({ isActive }) : children}
</Link>
);
}
If you're using Nextjs AppRouter 14* (don't know if it works with 13) and you have nested routes here is how to deal with it. Example:
- if you've a route like this:
/dashboard/contacts/
or/dashboard/contacts/new
,useSelectedLayoutSegment
will give you: contacts as a segment - if the route is
dashboard
you'll havenull
import { usePathname, useSelectedLayoutSegment } from "next/navigation"
const pathname = usePathname()
const segment = useSelectedLayoutSegment()
const isActive = segment
? href.toString().includes(segment)
: pathname === href
return (
<Link href={href} className={cn("flex items-center px-4 py-2 text-gray-500 dark:text-gray-200 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700",
isActive && "text-primary border bg-gray-100 dark:border-gray-600 dark:text-primary dark:bg-gray-700")}>
<Icon className="h-5 w-5 mr-3" />
<span>{title}</span>
</Link>
)
Hope it helps
本文标签: javascriptAdding active class for nav link in NextjsStack Overflow
版权声明:本文标题:javascript - Adding active class for nav link in Next.js - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741125164a2344040.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论