When I first switched to the App Router in Next.js 13, I ran into something that left me scratching my head. I was trying to highlight the active link in my navbar something super common. In previous versions of Next.js, I used useRouter()
from next/router
, which gave me the handy pathname
property. But when I updated my code using next/navigation
(as recommended for the App Directory), I realized:
Here’s how I discovered the issue, how I fixed it, and how I made the solution more reusable and powerful. Let’s break it down.
Original Code with Error
Here’s the initial code I wrote, expecting pathname
to be available:
Link from "next/link";
import { useRouter } from 'next/navigation'; // Correct for App Router
const StyledNavLink: React.FC<NavLinkProps> = ({ to, children }) => {
const router = useRouter(); // 🚨 'pathname' is not available here
return (
<Link href={to} className={getNavLinkClass({ isActive: router.pathname === to })}>
{children}
</Link>
);
};
Error:
'pathname' does not exist on type 'AppRouterInstance'
Why This Happened:
In the new App Router, useRouter()
gives us navigation methods (like push()
or replace()
), but it no longer gives access to the current pathname. Instead, we need to use a different hook: usePathname()
.
Corrected Code Using usePathname()
Once I discovered the correct hook, I updated the code like this:
Link from "next/link";
import { usePathname } from 'next/navigation';
interface NavLinkProps {
to: string;
children: React.ReactNode;
}
const StyledNavLink: React.FC<NavLinkProps> = ({ to, children }) => {
const pathname = usePathname(); // This gives us the current path
const isActive = pathname === to;
return (
<Link
href={to}
className={isActive ? "text-blue-500 font-bold" : "text-gray-500"}
>
{children}
</Link>
);
};
export default StyledNavLink;
Explanation:
usePathname()
is the new go-to method for getting the current route in the App Directory.- It returns a string like
"/about"
or"/contact"
. - I compared this with the
to
prop to decide whether the link is active, and applied conditional styling.
Bonus Practice Partial Match & Custom Classes
I wanted a bit more control. What if I’m on a nested route like /blog/my-post
and still want the /blog
link to be highlighted?
Here’s the upgraded version of the component that:
- Supports partial matching
- Allows custom active/inactive class names
- Adds an optional
exact
prop for strict matching
Link from "next/link";
import { usePathname } from 'next/navigation';
import clsx from 'clsx'; // Optional utility for clean class handling
interface NavLinkProps {
to: string;
children: React.ReactNode;
exact?: boolean;
activeClass?: string;
inactiveClass?: string;
}
const StyledNavLink: React.FC<NavLinkProps> = ({
to,
children,
exact = false,
activeClass = "text-blue-600 font-bold",
inactiveClass = "text-gray-600",
}) => {
const pathname = usePathname();
const isActive = exact ? pathname === to : pathname.startsWith(to);
return (
<Link href={to} className={clsx(isActive ? activeClass : inactiveClass)}>
{children}
</Link>
);
};
export default StyledNavLink;
What I Added:
exact
: Ensures a strict match (/blog
!==/blog/post
)activeClass
/inactiveClass
: Lets me fully control the styling from outsideclsx
: A great little package that makes combining classes neater
How I Used It in My App
<StyledNavLink to="/" exact>Home</StyledNavLink>
<StyledNavLink to="/about">About</StyledNavLink>
<StyledNavLink to="/blog">Blog</StyledNavLink>
This setup made my navbar dynamic and responsive to the current route, just like I wanted—clean, elegant, and App Router–friendly.
Final Thoughts
Switching to the App Directory in Next.js 13 definitely comes with a bit of a learning curve. But once I understood that useRouter()
was now navigation-only and usePathname()
was my new best friend, things clicked.