96

So basically I have a server component in app dir and I want to get the pathname. I tried to do it using window.location but it does not work. Is there any way I can do this?

5
  • I don't think window exists on the serverside. Consider using the path module and dirname? Commented Feb 6, 2023 at 14:30
  • I already know that window does not exists, but thanks anyways Commented Feb 6, 2023 at 14:38
  • 1
    found some info on answer by @and-yet-it-compiles in documentation "Using cookies() or headers() in a Server Component will opt the whole route into dynamic rendering at request time." source: nextjs.org/docs/app/building-your-application/rendering/… Commented May 31, 2023 at 8:07
  • just add {params} to the server function and you will get the pathname. Commented Dec 28, 2023 at 13:34
  • nextjs15 server component: const symbols = Object.getOwnPropertySymbols(globalThis); const needed = symbols[symbols.length - 1]; // @ts-ignore console.log(Object.keys(globalThis[needed].clientReferenceManifestsPerPage)[0]); Commented Mar 3 at 14:25

7 Answers 7

51

Take a look at https://github.com/vercel/next.js/issues/43704 , pauliusuza solved this issue

You can do a workaround using a middleware

import { NextResponse } from 'next/server';

export function middleware(request: Request) {

  // Store current request url in a custom header, which you can read later
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-url', request.url);

  return NextResponse.next({
    request: {
      // Apply new request headers
      headers: requestHeaders,
    }
  });
}

Then use it from inside of a root layout:

import { headers } from 'next/headers';

export default function RootLayout() {
  const headersList = headers();
  // read the custom x-url header
  const header_url = headersList.get('x-url') || "";
}

Hope that helps

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

7 Comments

One caveat here though. This will completely disable SSG for the page. More information could be found nextjs.org/docs/app/building-your-application/rendering/….
In my case, I got the pathname using the x-invoke-path header
ATTENTION : doesn't work in production i iis I don't know about others like linux or ...
This doesn't work for the root layout after an in-page navigation. For example, if we directly go to http://localhost:3000/fa it works. But if then we navigate to /en page (e.g. by clicking on <Link href='/en' ...) the middleware works, but the layout component doesn't rerender!
It doesn't work after in-page nav bc by design middleware stays mounted.
Omfg the design on this thing. You can't use middleware for everything. For example if you need to refresh an access token through an async call, the page needs to be blocked on layout or page level. The middleware doesn't block. So then we're back to nowhere.
|
36

Alternatively, I think an even easier solution would be to use Next's headers already present in the request, like host or refer.

Example:

import { headers } from 'next/headers';

export default function Navbar() {
    const headersList = headers();
    const domain = headersList.get('host') || "";
    const fullUrl = headersList.get('referer') || "";

    console.log(fullUrl);
}

2 Comments

referer is the URL that you came from, not the URL of the page you're on.
also yout can get url pathname using new URL(fullurl).pathname :)
28

NextJS v14 Serverside get Pathname

 import { headers } from "next/headers";

 const heads = headers()

 const pathname = heads.get('next-url')

Heads-up: it might have performance implications. From the NextJS documentation:

Good to know:

headers() is a Dynamic Function whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into dynamic rendering at request time.

About dynamic rendering

Important! The VP of Product @vercel discourages the use of the next-url header

8 Comments

Thank you sir. My use case was to dynamically change the styling based on the pathName
In Nextjs v14.0.2. this header is not available. See github.com/vercel/next.js/issues/58542
I'm also on next 14.0.4 and while I have seen the next-url header sometimes, it is not working reliably. Not in dev mode and not in production on vercel.
This was discouraged by the VP of Vercel. See here
const heads = await headers() const url= 'http://'+heads.get('host')
|
14

This works for me

import { headers } from "next/headers";

const headersList = headers();
const domain = headersList.get("x-forwarded-host") || "";
const protocol = headersList.get("x-forwarded-proto") || "";
const pathname = headersList.get("x-invoke-path") || "";

4 Comments

Search params are missing when use this approach
This no longer work in v14.0.2. What version are you using?
It doesn't work anymore
const heads = await headers() ....
10

create a middleware.ts in the src dir of your app, it would be responsible for urls of your application, i added some other useful properties such as origin which is good for calling internal api and the pathname for accessing the path of your component; note that it only can be used inside server component

import { NextResponse } from 'next/server';

export function middleware(request: Request) {

const url = new URL(request.url);
const origin = url.origin;
const pathname = url.pathname;
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-url', request.url);
requestHeaders.set('x-origin', origin);
requestHeaders.set('x-pathname', pathname);

return NextResponse.next({
    request: {
        headers: requestHeaders,
    }
});
}

and in your server components use it like

import { headers } from "next/headers";

export default async function page() {

    const headersList = headers()
    const header_url = headersList.get('x-url') || "";
    const pathname = headersList.get('x-pathname');
    const origin_url = headersList.get('x-origin');

}
based on https://github.com/vercel/next.js/issues/43704

Comments

8

This finds the pathname assuming you got a referer cookie:

import { headers } from 'next/headers';

const domain = headersList.get('host') || "";
const fullUrl = headersList.get('referer') || "";
const [,pathname] = fullUrl.match( new RegExp(`https?:\/\/${domain}(.*)`))||[];

console.log(pathname);

3 Comments

Sometimes it doesn't work. The rest of the answers did not work at all. my next version: 14.2.3
Not a stable solution, NextJs 14 here
its is giving the previous pathname
4

This answer is almost correct . Using app directory with src pattern I realized that I need to change the middleware.ts placement like the following.

.
├── src
│   ├── app
│   └── middleware.ts

1 Comment

correct . nice thank u

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.