Correct way to handle logged in user in site header?
Answered
Riday π posted this in #help-forum
What is the correct way to handle logged in user in the header/nav?
To check whether a user is logged in or not, we generally need to get the cookies / headers and do some stuff with that. When we use any of these functions, Next.js will automatically make that page as dynamic.
But, we generally add the header in the root layout. This seems to make every single page dynamic in my app. But, I don't need pages like about us, contact us, terms and conditions, etc to be dynamic.
I just want them to have a dynamic header that shows whether the user is logged in or not.
To check whether a user is logged in or not, we generally need to get the cookies / headers and do some stuff with that. When we use any of these functions, Next.js will automatically make that page as dynamic.
But, we generally add the header in the root layout. This seems to make every single page dynamic in my app. But, I don't need pages like about us, contact us, terms and conditions, etc to be dynamic.
I just want them to have a dynamic header that shows whether the user is logged in or not.
Answered by Riday π
// layout.tsx, server component
<Header throwaway={Date.now()} />
// Header.tsx, client-component
function Header({ throwaway }: HeaderProps) {
const { data } = useSWR(`/api/session?t=${throwaway}`);
}
30 Replies
I have tried making the header as a client component, but revalidatePath() doesn't seem to revalidate the client side cache. I need a way to revalidate client side cache from the server or something like that
@Riday π What is the correct way to handle logged in user in the header/nav?
To check whether a user is logged in or not, we generally need to get the cookies / headers and do some stuff with that. When we use any of these functions, Next.js will automatically make that page as dynamic.
But, we generally add the header in the root layout. This seems to make every single page dynamic in my app. But, I don't need pages like about us, contact us, terms and conditions, etc to be dynamic.
I just want them to have a dynamic header that shows whether the user is logged in or not.
Id say the best way is PPR, which allows dynamic slots in an otherwise static page. But PPR is unstable and very buggy.
Many people just make all pages dynamic. Like the good old days, all pages are dynamic. Of course this only applies when you have a fast backend.
I personally just client-side render the auth state, using something like the useSession hook of next-auth.
Many people just make all pages dynamic. Like the good old days, all pages are dynamic. Of course this only applies when you have a fast backend.
I personally just client-side render the auth state, using something like the useSession hook of next-auth.
@joulev Id say the best way is PPR, which allows dynamic slots in an otherwise static page. But PPR is unstable and very buggy.
Many people just make all pages dynamic. Like the good old days, all pages are dynamic. Of course this only applies when you have a fast backend.
I personally just client-side render the auth state, using something like the useSession hook of next-auth.
Well, I guess everything dynamic it is, then.
Or I need to figure out a way to reset client side router state when logging in / logging out
@Riday π Or I need to figure out a way to reset client side router state when logging in / logging out
Uhm when you implement client side rendering, shouldnβt it just work? How are you implementing client side rendering?
@joulev Uhm when you implement client side rendering, shouldnβt it just work? How are you implementing client side rendering?
// In client component, root layout > Header.tsx
const session = await fetch('/api/session');
But when I use revalidatePath(...), it's not revalidating the client side cache
So, like when I login / logout, the header still shows stale data till I refresh the page
@Riday π Oh wait, yes. It was in use effect
Alright.
1. The easy solution is to ditch that and use SWR/react-query to do client side rendering. Then when logging out, simply bust the client side cache that caches the auth state.
2. The dependency-less solution would be to make a client side state in a way that you can use it to force the useEffect to rerun again. This method is not pretty. Just use method 1, which is how you are recommended to do client side data fetching in react anywhere.
1. The easy solution is to ditch that and use SWR/react-query to do client side rendering. Then when logging out, simply bust the client side cache that caches the auth state.
2. The dependency-less solution would be to make a client side state in a way that you can use it to force the useEffect to rerun again. This method is not pretty. Just use method 1, which is how you are recommended to do client side data fetching in react anywhere.
@joulev Alright.
1. The easy solution is to ditch that and use SWR/react-query to do client side rendering. Then when logging out, simply bust the client side cache that caches the auth state.
2. The dependency-less solution would be to make a client side state in a way that you can use it to force the useEffect to rerun again. This method is not pretty. Just use method 1, which is how you are recommended to do client side data fetching in react anywhere.
I got the useSWR part, but how do I handle cache busting? Every aspect of my auth is server side:
LoginForm > loginAction > redirect('/dashboard')
LogoutButton > logoutAction > redirect('/login')
LoginForm > loginAction > redirect('/dashboard')
LogoutButton > logoutAction > redirect('/login')
I guess in that case I'll have to handle redirects on the client side?
With OAuth, it's even more difficult since there is nothing on the client side
@Riday π I got the useSWR part, but how do I handle cache busting? Every aspect of my auth is server side:
LoginForm > loginAction > redirect('/dashboard')
LogoutButton > logoutAction > redirect('/login')
SWR has this https://swr.vercel.app/docs/mutation.
react-query has something similar too.
Just run this in the onClick of the button by which the user log in/out.
react-query has something similar too.
Just run this in the onClick of the button by which the user log in/out.
Yes your login/logout buttons need to be client components, but that shouldnβt be a problem.
@joulev SWR has this https://swr.vercel.app/docs/mutation.
react-query has something similar too.
Just run this in the onClick of the button by which the user log in/out.
But, won't this refetch before the session change actually takes place?
@joulev Alright.
1. The easy solution is to ditch that and use SWR/react-query to do client side rendering. Then when logging out, simply bust the client side cache that caches the auth state.
2. The dependency-less solution would be to make a client side state in a way that you can use it to force the useEffect to rerun again. This method is not pretty. Just use method 1, which is how you are recommended to do client side data fetching in react anywhere.
American Crow
i love how you provided 2 solutions and solution #2 tells you to use #1 π π―
@Riday π But, won't this refetch before the session change actually takes place?
American Crow
OMG is this the first use case of
after
in the help forum?? Which is even more experimental (Nextjs 15 RC) than PPR :D. I am not helping sorry, i gonna shut up nowOkay, I just thought of another (possibly bad) idea - I could pass a throwaway prop from the layout component on the server to the client-side header component that would force it to refetch or re-render. Something like Math.random() or Date.now()
@Riday π But, won't this refetch before the session change actually takes place?
Good point. I suppose something like this should work
<form action={async formData => {
await logout(formData)
mutate("/api/session")
router.push("/")
}}>
<form action={async formData => {
await logout(formData)
mutate("/api/session")
router.push("/")
}}>
@Riday π Okay, I just thought of another (possibly bad) idea - I could pass a throwaway prop from the layout component on the server to the client-side header component that would force it to refetch or re-render. Something like Math.random() or Date.now()
Yes thatβs my #2. Requires more code, but it works yes
How does it require more code? Assuming that my current code has everything working on the server side. With this approach, I just have to write like 1 line of code and it should work... Right?
@Riday π How does it require more code? Assuming that my current code has everything working on the server side. With this approach, I just have to write like 1 line of code and it should work... Right?
Oh I had a different thing in mind. I thought you meant it in a completely client side way: a context+state in the root layout with a key, you change the key on login/logout and in the header you listen to changes in the key to rerun useEffect.
Yes, with server rendering it does look simpler. Try it, I think it does work yes
Yes, with server rendering it does look simpler. Try it, I think it does work yes
Okay
@joulev Oh I had a different thing in mind. I thought you meant it in a completely client side way: a context+state in the root layout with a key, you change the key on login/logout and in the header you listen to changes in the key to rerun useEffect.
Yes, with server rendering it does look simpler. Try it, I think it does work yes
It works! Thanks for brainstorming with me, lol
// layout.tsx, server component
<Header throwaway={Date.now()} />
// Header.tsx, client-component
function Header({ throwaway }: HeaderProps) {
const { data } = useSWR(`/api/session?t=${throwaway}`);
}
Answer