Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A way to access the params and pathname in all server components #46618

Closed
joulev opened this issue Mar 1, 2023 · 3 comments
Closed

A way to access the params and pathname in all server components #46618

joulev opened this issue Mar 1, 2023 · 3 comments
Labels
TypeScript Related to types with Next.js.

Comments

@joulev
Copy link
Contributor

joulev commented Mar 1, 2023

Describe the feature you'd like to request

Currently, inside server components, while it's possible to get the params in dynamic pages, and subsequently the pathname (params + hardcoding) in all pages, there isn't an easy way to access the information in all server components similar to usePathname in client components.

So sometimes I want to do something like this

// server component, reused in many places
function ProductCard() {
  const pathname = "????";
  return (
    <div>
      ...
      {pathname === "/special-page" && <SpecialButton />}
    </div>
  )
}

but since I don't have pathname, I have to fallback to client components just to use usePathname. While I'm fine with it so far for my use case, there are definitely cases where we want to use RSC instead (I don't want to send SpecialButton to pages that don't need it, for example).

Describe the solution you'd like

A way to easily access the current pathname and params (and search params as well, maybe?) in any RSC.

There are several APIs I can imagine, but I don't know about their feasibilities:

  • Add a special prop like _next to all server components that contains the actual pathname and params that the server component is rendered in. For TypeScript, we can have a type NextServerComponent

    export type NextServerComponent<P = unknown> = P & {
      _next: {
        pathname: string;
        // or segments: string[];
        params: ParsedUrlQuery;
      }
    }
  • Add a getPathname() and getParams() function to "next/navigation" that can be used in server components.

Describe alternatives you've considered

N/A

@github-actions github-actions bot added the TypeScript Related to types with Next.js. label Mar 1, 2023
@Fredkiss3
Copy link
Contributor

I don't know if it is the same, but there is a similar issue : #43704

But the reasonning is that you can't access the pathname in Layouts as they don't rerender between navigations, so they can't be aware of the current pathname, same for searchParams. You can have access to searchParams in pages however, for path params (like [id]) you have access to them in layouts, but the layout can only have access to the params in its level :

Say you are on a route /blog/category-one/slug-one;

  • A layout at app/blog/[category-id]/layout.tsx can only have access to [category-id]
  • A layout or page at app/blog/[category-id]/[slug-id]/{layout.tsx,page.tsx} can have access to both [category-id] & [slug-id].

With that i think you can reconstruct the current pathname.
Of if you really want to pass it to every page, you can use middleware and pass the URL as a header :

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// the following code is taken from : https://nextjs.org/docs/advanced-features/middleware#setting-headers
export function middleware(request: NextRequest) {
  return NextResponse.next({
     request: {
        // New request headers
        'x-pathname': request.nextUrl.pathname,
     },
  });
}

// the following code has been copied from https://nextjs.org/docs/advanced-features/middleware#matcher
export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}

// app/some/where/page.tsx
import { headers } from 'next/headers';

export default async function Page() {
  const pathname = headers.get('x-pathname');
   
  return <>{/*... your page code */}</>
}

@joulev
Copy link
Contributor Author

joulev commented Mar 1, 2023

they don't rerender between navigations

Ohh, I see. Somehow this completely escaped my mind.

Yeah, in this case, there isn't much use for this feature anymore, as reused components like ProductCard above can always have an additional prop params that is passed directly from page files, if the user wants it.

A different use case I thought of was some special element for specific pages inside the layout, which can be solved with route groups and additional layout files. I considered such approaches to be too much code, but now thinking back about it it's the only logical approach.

Once again, thanks for the explanation!

@joulev joulev closed this as completed Mar 1, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Apr 1, 2023

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
TypeScript Related to types with Next.js.
Projects
None yet
Development

No branches or pull requests

2 participants