Active Links #43619
lawrencejob
started this conversation in
Ideas
Active Links
#43619
Replies: 2 comments
-
In addition, in case this helps anyone, you need to make this change to make the 'use client';
import { usePathname } from 'next/navigation';
import Link, { LinkProps } from 'next/link';
import React, { PropsWithChildren, useState, useEffect } from 'react';
type ActiveLinkProps = LinkProps & {
className?: string
activeClassName: string
}
const ActiveLink = ({
children,
activeClassName,
className,
...props
}: PropsWithChildren<ActiveLinkProps>) => {
const path = usePathname();
const [computedClassName, setComputedClassName] = useState(className);
useEffect(() => {
// 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;
const newClassName =
linkPathname === path
? `${className} ${activeClassName}`.trim()
: className;
if (newClassName !== computedClassName) {
setComputedClassName(newClassName);
}
}, [
path,
props.as,
props.href,
activeClassName,
className,
computedClassName,
]);
return (
<Link className={computedClassName} {...props}>
{children}
</Link>
);
}
export default ActiveLink From: https://github.com/vercel/next.js/blob/canary/examples/active-class-name/components/ActiveLink.tsx |
Beta Was this translation helpful? Give feedback.
0 replies
-
I came across this looking for something similar. However, I needed "use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Home, LineChart, Users, Calendar, Medal } from "lucide-react";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
const NavItems = [
{ href: "/admin", icon: Home, label: "Dashboard" },
{ href: "/admin/events", icon: Calendar, label: "Events" },
{ href: "/admin/teams", icon: Users, label: "Teams" },
{ href: "/admin/achievements", icon: Medal, label: "Achievements" },
{ href: "/admin/analytics", icon: LineChart, label: "Analytics" },
];
export default function LeftNav() {
const pathname = usePathname();
const pathParts = pathname.split("/");
return (
<div className="flex-1">
<nav className="grid items-start px-2 text-sm font-medium lg:px-4">
{NavItems.map(({ href, icon: Icon, label }) => {
const hrefParts = href.split("/");
const isActive =
pathParts[1] === hrefParts[1] && pathParts[2] === hrefParts[2];
return (
<Link
key={href}
href={href}
className={cn(
"flex items-center gap-4 rounded-xl px-3 py-2",
isActive
? "bg-muted text-foreground"
: "text-muted-foreground hover:text-foreground"
)}
>
<Icon className="h-5 w-5" />
{label}
{label === "Events" && (
<Badge className="ml-auto flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
6
</Badge>
)}
</Link>
);
})}
</nav>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Hi, just want to say thank you for an excellent framework. I've been using it since the beginning and eagerly adopting the new appDir features. Secondly, please forgive me if this has been raised elsewhere, I'm happy to have this deleted, although I have done a lot of searching and can't find anything.
In Angular, there's a feature that lets you specify a class to use if their equivalent of
<Link>
matches a route.Historically, when Next was about building whole pages from a layout (pre appDir) the absence of this feature was fairly easy to work around - I've seen a lot of apps use some form of component that inspects the URL to look for an exact match. However, in light of the tree-like behaviours of the new router in
appDir
mode, I believe that thenext/link
framework is better placed to abstract over all the router logic required to do that.This is because links in a
layout.tsx
file could be considered 'active' when the link matches even part of the URL.e.g.
When a viewer is looking at
/countries/united-states
:I would expect the 'countries' link to be styled differently to FAQs and Contact links, even though the countries link doesn't exactly match the 'countries/united-states' URL the user is currently on.
I do believe that it's possible to write third party code to wrap around the behaviour of
<Link>
to add this functionality in some kind of higher-order wrapper component, but I do think that makes for an unpleasant API and behaviour that's brittle to changes in the router and likely to break down edge case uses of the router.I am aware of the
<ActiveLink>
example, which does a good job of demonstrating the hoop that developers need to jump through. In its current form ActiveLink is also necessarily a client-side component due to its use of state and effect: I'm curious to know if this is problematic.I think it would be a great opportunity if there was framework-level support for this, but I'm happy to hear if you disagree, or if there's a discussion that's taken place about this that I'm not aware of.
Beta Was this translation helpful? Give feedback.
All reactions