65

I'm using the experimental app folder in Next.js 13, where they have replaced next/router with next/navigation, so I imported the useRouter hook accordingly. I do not see the property pathname in the router, which does exist in next/router's router.

Property 'pathname' does not exist on type 'AppRouterInstance'

My use case is to highlight the link in my navbar that the user is currently on. This is what I tried:

import Link from "next/link";
import { useRouter } from 'next/navigation';

const StyledNavLink: React.FC<NavLinkProps> = ({ to, children }) => {
  const router = useRouter();
  ...
  return (
    <Link href={to} className={getNavLinkClass({isActive: router.pathname === to})}>
      {children}
    </Link>
  );
};

Is there anything I can do to get the current path name or something else to add my classes to the active link?

1
  • 2
    you can use usePathname but it doesn't work with server components yet Commented Nov 26, 2022 at 18:11

9 Answers 9

110

When using the app router (directory), the pathname has its hook called usePathname. Here is a quote from the doc:

The usePathname hook allows you to read the current URL pathname from a Client Component.

Find below an example, and notice the 'use client' at the top:

'use client';

import { usePathname } from 'next/navigation';

export default function Page() {
  const pathname = usePathname();
  return <div>{pathname}</div>;
}

As of now, the doc doesn’t mention a way to get the pathname server side. However, you could use the technique inspired by this GitHub comment, combining Next.js middleware and request headers:

// middleware.js

import { NextResponse } from "next/server";

export function middleware(request) {
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set("x-pathname", request.nextUrl.pathname);

  return NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  });
}
// app/page.js

import { headers } from "next/headers";

export default async function Page() {
  const headersList = headers();

  return <div>{headersList.get("x-pathname")}</div>;
}

And if you are using Dynamic Routes (aka the [id] folders), and you want to know the value of the slug, check out this post.

Sign up to request clarification or add additional context in comments.

Comments

19

If you are looking to get the current active pathname on the server side component/page/layout, you can use headers from next/headers.

import { headers } from "next/headers";

export function Component() {
  const headersList = headers();
  const activePath = headersList.get("x-invoke-path");
  // const activeUrl = headersList.get("referer");
  // const activeHost = headersList.get("host");
  
  return (<div>{activePath}</div>)
}

7 Comments

Is it next.js setting this header?
Yes @D0m3 , it is set by next.js.
I didn't find x-invoke-path, but next-url works.
Actually this solution is not recommended by nextjs and does not work on vercel, see github.com/vercel/next.js/issues/54711#issuecomment-1697436921
Also, I believe this would de-opt you out of static rendering: nextjs.org/docs/app/building-your-application/rendering/…
|
5

If you want to get easily the current pathname on your client server component :

On Next js 13 or 14

/[my-dynamic-route]/page.jsx

export default function Page({ params }) {
  const pathname = params.my-dynamic-route;
  return (
    <div>
      <h1>{pathname}</h1>
    </div>
  );
}

It works like useSearchParams in the documentation Here

On Next js 15 it's asynchronous

We need to add await before params.

/[my-dynamic-route]/page.jsx

export default async function page({ params }) {
  const { my-dynamic-route } = await params; // Change 'my-dynamic-route' for your own route name
  return (
    <div>
      <h1>{my-dynamic-route}</h1>
    </div>
  );
}

Dynamic APIs are Asynchronous doc Here

1 Comment

The pathname we're looking for is a string (like '/dashboard/customers'), how does params help you get it?
3

To access the URL from server actions and components in your Next.js project, you can use the next-extra package.

First, install the package by running the following command:

npm install next-extra

Once installed, you can use the package as shown below:

import { headers } from 'next/headers';
import { pathname } from 'next-extra/pathname';

export default async function Layout({ children }: Readonly<{ children: React.ReactNode }>) {
  const base = `${headers().get('x-forwarded-proto')}://${headers().get('host')}`;
  const fullUrl = new URL(pathname(), base); // e.g., http://localhost:3000/some/path?a=1&b=2

  console.log(fullUrl);

  return children;
}

For the client components please refer to the official documentation: App Router: usePathname.

I hope this helps!

1 Comment

Thanks my friend, import { pathname } from 'next-extra/pathname'; this solved my problem with translation on custom page of not-found, in this function I don't have access to URL, in child header and footer, wich is ssr, I have translation fetch and for this fetch I need locale from URL path)
2

One way (albeit a bit hacky) to get the pathname on the server side which doesn't require middleware is answered here:

Nextjs13 app dir doesn't have a nice API for this yet. It'll be nice if the props passed down the segment.

For now, you can use children.props.childProp.segment to get the path of your route on the server.

Comments

2

In Nextjs 13+ with app router, there is better way to get "searchParams" like /search?q=xxxxx on the server side. See the code below.

You can make it better by having a type (instead of any) to define your searchParams, in my case url is my domain.com/search?q=my-term and I can access this as on the code below:

interface SearchProps {
    searchParams: any //define your type you expect in the url
}

export default async function Search({ searchParams }: SearchProps) {
    const { q } = searchParams
    const { posts, totalPages, totalPosts } = await searchPost(q)
    return (
    <main>
        <h1 className="text-4xl font-bold mt-4 mb-2">Search result</h1>
        

        <p className="my-4">Search term: {q}</p>

        <section className="my-4">
            {posts && posts.map((post: any) => <PostSmall key={post.id} post={post} />)}
        </section>
    </main>
    )
}

Comments

1

Can you use this code for server components:

import { headers } from 'next/headers'
import 'server-only'

export default function useServerPathname() {

    const headersList = headers()
        , origin = headersList.get('origin') || ''
        , referer = headersList.get('referer') || ''
        , path = referer.replace(origin, '')

    if (path) {
        return path
    }

    throw new Error()
}

Comments

0

Next.js has updated its documentation and explains here how you can use usePathname() to get the pathname in a client component:

'use client'
 
import { usePathname } from 'next/navigation'
import Link from 'next/link'
 
export function NavLinks() {
  const pathname = usePathname()
 
  return (
    <nav>
      <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        Home
      </Link>
 
      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        About
      </Link>
    </nav>
  )
}

Then use the component in a page.tsx or layout.tsx

import { NavLinks } from '@/app/ui/nav-links'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <NavLinks />
        <main>{children}</main>
      </body>
    </html>
  )
}

I tested it successfully in Next.js 13, 14 and 15.

Comments

-6

Here is how I do it through the server side:

export const POST = (request: Request) => {
  const pathname = new URL(request.url).pathname;
  return new Response(`You requested from ${pathname}`, { status: 200 });
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.