Pathname from layout.ts at new app structure (nextjs 13) #43657
-
Hi! I'm trying to write a simple "middleware" to protect some routes in a demo app using nextjs 13. The way I'm going about this is trying to get the current path I'm loading into via serverside in a layout.ts at "/" level. Since everything that happens inside this layout.ts happens in all pages, is server side and I'm already getting the user info, so I know if he is logged in or not, it seemed like a good idea. However I could not find a way to get the current path to check if I'm not already at a login page or not. The ideia is simple:
Is there a way to get the current path server side in a layout? |
Beta Was this translation helpful? Give feedback.
Replies: 13 comments 25 replies
-
Looking for it too 😢 |
Beta Was this translation helpful? Give feedback.
-
I had a similar problem. I was thinking that it would be similar to access to the request headers (but the requestStore does not contain that information) So probably we need a new server component function :( |
Beta Was this translation helpful? Give feedback.
-
I need the pathname for highlighting current page in header navigation. |
Beta Was this translation helpful? Give feedback.
-
Took me a while to find: |
Beta Was this translation helpful? Give feedback.
-
Need the same to get the current page url for SEO purpose. |
Beta Was this translation helpful? Give feedback.
-
Hi! Couple days ago I faced the same issue and didn't find anything about that. So here is my solution: In the // middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export default function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-next-pathname', request.nextUrl.pathname);
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}; // layout.tsx
import { PropsWithChildren } from 'react';
import getUser from '@/lib/get-user';
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
export default async function Layout({ children }: PropsWithChildren) {
const user = await getUser();
const pathname = headers().get('x-next-pathname') as string;
if (!user && pathname !== '/login') {
redirect('/login');
}
if (user && pathname === '/login') {
redirect('/');
}
return (
// ...
)
} |
Beta Was this translation helpful? Give feedback.
-
This is what has worked for me:
|
Beta Was this translation helpful? Give feedback.
-
If this helps anyone in the future. My stack is:
// /app/layout.tsx
import './globals.css'
import React from 'react'
import { Inter } from 'next/font/google'
import {Providers} from './providers'
import {cookies} from 'next/headers'
import MainNav from "@/components/main-nav";
import UserNav from "@/components/user-nav";
import { headers } from "next/headers";
import AuthActionButton from "@/components/AuthActionButton";
const inter = Inter({ subsets: ['latin'] })
export default async function RootLayout({
children,
}: {
children: React.ReactNode
}): Promise<Element> {
const isLoggedIn: boolean = cookies().has('pb_auth')
return (
<html lang="en" className="h-full" suppressHydrationWarning>
<body className="h-full">
<Providers>
<div className="border-b">
<div className="flex h-16 items-center px-4">
<MainNav className="mx-6"/>
<div className="ml-auto flex items-center space-x-4">
{!isLoggedIn && <AuthActionButton />}
{isLoggedIn && <UserNav />}
</div>
</div>
</div>
<main className="mx-auto">{children}</main>
</Providers>
</body>
</html>
)
} // /components/AuthActionButton.tsx
'use client'
import {Link} from "@nextui-org/react";
import {usePathname} from "next/navigation";
import {JSX} from "react";
import {Button} from "@/components/ui/button";
export default function AuthActionButton(): JSX.Element {
const pathname: string = usePathname()
return (
<Link href={pathname === '/auth/signin' ? '/auth/signup' : '/api/auth/signin'}>
<Button>{pathname === '/auth/signin' ? 'Sign up' : 'Sign in'}</Button>
</Link>
)
} |
Beta Was this translation helpful? Give feedback.
-
I cant believe this is not a thing and that there is no feedback from nextjs. Surely the server should know what route is being hit otherwise how does it know which pages to render? Doesnt make sense that you cannot get the url or pathname of a route in a server component. How do you use this thing: |
Beta Was this translation helpful? Give feedback.
-
In the same vein as path names not being available in layouts... query params too:
To me, this makes nested layouts that do anything beyond rendering HTML i.e. data fetching completely pointless because you can't use the url to drive the state of your application for your data fetching.. like every good web app should do. Back to client side data fetching to avoid the chaos |
Beta Was this translation helpful? Give feedback.
-
If you need to access to the pathname outside the 'use client'
import { usePathname } from 'next/navigation'
// This a client component, still prerendered
export function Pathname({ children }) {
const pathname = usePathname()
return (
<div>
<p>Path: {pathname}</p>
{children}
</div>
)
}
---
// Some nested layout where you don't have `params` and `searchParams` as props
// The child page can be a server component!
export default function DashboardLayout({ children }) {
return (
<div>
<Pathname>
{children}
</Pathname>
</div>
)
}
|
Beta Was this translation helpful? Give feedback.
-
What i do is to write a client component inside the |
Beta Was this translation helpful? Give feedback.
-
I don't understand why there can't be a function from export default async function Layout({children}) {
const path = pathname();
const session = await getSession();
if (!session) {
redirect(`/login?redirect=${path}`);
}
return <div>{children}</div>
} I don't want to use middleware because I don't want it to be global for all pages and have to select which routes it runs for, that's a nighmare to maintain. I also don't really trust the middleware runtime at this point. Having this in RSC would be so simple IMO. |
Beta Was this translation helpful? Give feedback.
If you need to access to the pathname outside the
page
, the solution is tousePathname()
.