[GH-ISSUE #1006] useSession() not always triggering a state change #25867

Closed
opened 2026-04-17 16:09:56 -05:00 by GiteaMirror · 76 comments
Owner

Originally created by @k3dom on GitHub (Dec 23, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1006

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Sign in with email and password
  2. Use useSession() in the following component:
export function AuthLayout({ children }: AppLayoutProps) {
  const sidebar = useConfigStore((state) => state.sidebar)
  const { data: session } = useSession()

  return (
    <SidebarProvider defaultOpen={sidebar}>
      <Sidebar collapsible="icon" variant="inset">
        <SidebarHeader>Test</SidebarHeader>
        <SidebarContent>
          <NavMain />
        </SidebarContent>
        <SidebarFooter>
          <NavUser
            name={session?.user.name ?? ''}
            email={session?.user.email ?? ''}
          />
        </SidebarFooter>
        <SidebarRail />
      </Sidebar>
      <SidebarInset>
        <NavHeader />
        <div className="m-4 mt-0 h-full">{children}</div>
      </SidebarInset>
    </SidebarProvider>
  )
}

Current vs. Expected behavior

  • Current:
    The session state always starts with null (as it should). It then does a network request to get-session which succeeds. The problem now is that the state does not update maybe 50% to the time resulting in me not being able to show the user's name in this example. If I then trigger a component rerender by e.g. opening the navbar, the session information is properly reflected in the UI

  • Expected:
    I expect the state to always update after a successfull get-session call

What version of Better Auth are you using?

1.1.1

Provide environment information

OS: Linux 6.12.4-arch1-1
Browser: Chrome 130.0.6723.116

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

betterAuth({
      trustedOrigins: c.env.BETTER_AUTH_TRUSTED_ORIGINS.split(','),
      database: drizzleAdapter(c.get('db'), {
        provider: 'pg',
        usePlural: true,
      }),
      emailAndPassword: {
        enabled: true,
      },
      advanced: {
        cookiePrefix: 'viaptic',
      },
    })

Additional context

I tested this in both production and development and on both Chrome and Firefox. The issue persists everywhere

Originally created by @k3dom on GitHub (Dec 23, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1006 ### Is this suited for github? - [X] Yes, this is suited for github ### To Reproduce 1. Sign in with email and password 2. Use `useSession()` in the following component: ```ts export function AuthLayout({ children }: AppLayoutProps) { const sidebar = useConfigStore((state) => state.sidebar) const { data: session } = useSession() return ( <SidebarProvider defaultOpen={sidebar}> <Sidebar collapsible="icon" variant="inset"> <SidebarHeader>Test</SidebarHeader> <SidebarContent> <NavMain /> </SidebarContent> <SidebarFooter> <NavUser name={session?.user.name ?? ''} email={session?.user.email ?? ''} /> </SidebarFooter> <SidebarRail /> </Sidebar> <SidebarInset> <NavHeader /> <div className="m-4 mt-0 h-full">{children}</div> </SidebarInset> </SidebarProvider> ) } ``` ### Current vs. Expected behavior - **Current**: The session state always starts with `null` (as it should). It then does a network request to `get-session` which succeeds. The problem now is that the state does not update maybe 50% to the time resulting in me not being able to show the user's name in this example. If I then trigger a component rerender by e.g. opening the navbar, the session information is properly reflected in the UI - **Expected**: I expect the state to always update after a successfull `get-session` call ### What version of Better Auth are you using? 1.1.1 ### Provide environment information ```bash OS: Linux 6.12.4-arch1-1 Browser: Chrome 130.0.6723.116 ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript betterAuth({ trustedOrigins: c.env.BETTER_AUTH_TRUSTED_ORIGINS.split(','), database: drizzleAdapter(c.get('db'), { provider: 'pg', usePlural: true, }), emailAndPassword: { enabled: true, }, advanced: { cookiePrefix: 'viaptic', }, }) ``` ### Additional context I tested this in both production and development and on both Chrome and Firefox. The issue persists everywhere
GiteaMirror added the lockedbug labels 2026-04-17 16:09:56 -05:00
Author
Owner

@cem2ran commented on GitHub (Dec 25, 2024):

I'm also seeing this. version 1.1.3. Not seeing useSession updating after logging in with apple in expo app.

<!-- gh-comment-id:2561698702 --> @cem2ran commented on GitHub (Dec 25, 2024): I'm also seeing this. version 1.1.3. Not seeing useSession updating after logging in with apple in expo app.
Author
Owner

@Nicolab commented on GitHub (Dec 30, 2024):

Because you should use useEffect to trigger a React rerender when the session object change

<!-- gh-comment-id:2565539539 --> @Nicolab commented on GitHub (Dec 30, 2024): Because you should use useEffect to trigger a React rerender when the session object change
Author
Owner

@k3dom commented on GitHub (Dec 30, 2024):

Because you should use useEffect to trigger a React rerender when the session object change

Thats not how reactive state works. The session object itself is supposed to be reactive and trigger a rerender on change. If you put the session in useEffect like so:

useEffect(() =>console.log(session), [session])

You will simply get the exact same behaviour which is it getting printed only 50% of the time.

<!-- gh-comment-id:2565542759 --> @k3dom commented on GitHub (Dec 30, 2024): > Because you should use useEffect to trigger a React rerender when the session object change Thats not how reactive state works. The session object itself is supposed to be reactive and trigger a rerender on change. If you put the session in `useEffect` like so: ```ts useEffect(() =>console.log(session), [session]) ``` You will simply get the exact same behaviour which is it getting printed only 50% of the time.
Author
Owner

@crackedeng commented on GitHub (Dec 31, 2024):

+1 also happening with expo

<!-- gh-comment-id:2566088078 --> @crackedeng commented on GitHub (Dec 31, 2024): +1 also happening with expo
Author
Owner

@statusunknown418 commented on GitHub (Jan 2, 2025):

+1 to this, also the expo adapter is not redirecting the users after signin wih social

<!-- gh-comment-id:2568116917 --> @statusunknown418 commented on GitHub (Jan 2, 2025): +1 to this, also the expo adapter is not redirecting the users after signin wih social
Author
Owner

@mbrimmer83 commented on GitHub (Jan 20, 2025):

+1 on Expo 52. I'm running into this as well on expo. I tried debugging, but wasn't able to find anything wrong with how nanostores or the react Store were handling reactivity.

<!-- gh-comment-id:2603167754 --> @mbrimmer83 commented on GitHub (Jan 20, 2025): +1 on Expo 52. I'm running into this as well on expo. I tried debugging, but wasn't able to find anything wrong with how `nanostores` or the react `Store` were handling reactivity.
Author
Owner

@eliabexp commented on GitHub (Jan 22, 2025):

+1 here, using tradicional email & password method

<!-- gh-comment-id:2608086228 --> @eliabexp commented on GitHub (Jan 22, 2025): +1 here, using tradicional email & password method
Author
Owner

@arxk commented on GitHub (Jan 30, 2025):

+1. This issue is still present.. This should be fixed.

<!-- gh-comment-id:2625199324 --> @arxk commented on GitHub (Jan 30, 2025): +1. This issue is still present.. This should be fixed.
Author
Owner

@AkshatKejriwal commented on GitHub (Feb 4, 2025):

+1, can confirm that it is still present.

<!-- gh-comment-id:2635026516 --> @AkshatKejriwal commented on GitHub (Feb 4, 2025): +1, can confirm that it is still present.
Author
Owner

@samcxps commented on GitHub (Feb 5, 2025):

+1 on 1.1.16

This issue is a dealbreaker for client-side only applications (i.e. Expo, Vite) where server-side auth checks aren't possible.

<!-- gh-comment-id:2638210139 --> @samcxps commented on GitHub (Feb 5, 2025): +1 on 1.1.16 This issue is a dealbreaker for client-side only applications (i.e. Expo, Vite) where server-side auth checks aren't possible.
Author
Owner

@vhondarev commented on GitHub (Feb 8, 2025):

+1 on 1.1.15

This might be overkill but as a workaround try get-session

<!-- gh-comment-id:2644436006 --> @vhondarev commented on GitHub (Feb 8, 2025): +1 on 1.1.15 This might be overkill but as a workaround try [get-session](https://github.com/better-auth/better-auth/blob/aaa5c60a037bc7486d45e16264d2672b6e82f064/docs/content/docs/basic-usage.mdx#get-session)
Author
Owner

@ruaanetwork commented on GitHub (Feb 14, 2025):

+1 on 1.1.15 also

<!-- gh-comment-id:2660448023 --> @ruaanetwork commented on GitHub (Feb 14, 2025): +1 on 1.1.15 also
Author
Owner

@fullyDeepak commented on GitHub (Feb 15, 2025):

Here is the workaround, it is not ideal but it works :)
Paste this in the same component where you are using useSession and it will force a re-render.

const [num, setNum] = useState(1);
  useEffect(() => {
    if (num < 2) {
      setNum(num + 1);
    }
  }, [num]);
<!-- gh-comment-id:2660760818 --> @fullyDeepak commented on GitHub (Feb 15, 2025): Here is the workaround, it is not ideal but it works :) Paste this in the same component where you are using useSession and it will force a re-render. ``` const [num, setNum] = useState(1); useEffect(() => { if (num < 2) { setNum(num + 1); } }, [num]); ```
Author
Owner

@amosbastian commented on GitHub (Feb 17, 2025):

This issue is a dealbreaker for client-side only applications (i.e. Expo, Vite) where server-side auth checks aren't possible.

It's not fixed in the beta either and I refuse to use something like Next.js for an app behind auth 😆

<!-- gh-comment-id:2663449604 --> @amosbastian commented on GitHub (Feb 17, 2025): > This issue is a dealbreaker for client-side only applications (i.e. Expo, Vite) where server-side auth checks aren't possible. It's not fixed in the beta either and I refuse to use something like Next.js for an app behind auth 😆
Author
Owner

@arthureberledev commented on GitHub (Feb 20, 2025):

I am using react (vite) with react query and this was my solution:

async function getSession() {
  return await authClient.getSession();
}

export function MyComponent() {
  const { data: session } = useQuery({
    queryKey: ["session"],
    queryFn: getSession,
  });

  [...]
}
<!-- gh-comment-id:2671090878 --> @arthureberledev commented on GitHub (Feb 20, 2025): I am using react (vite) with react query and this was my solution: ``` async function getSession() { return await authClient.getSession(); } export function MyComponent() { const { data: session } = useQuery({ queryKey: ["session"], queryFn: getSession, }); [...] } ```
Author
Owner

@JosipPardon commented on GitHub (Feb 21, 2025):

I have solution.

When component with useSession is mounted, it always fetches session with api/auth/get-session request, but sometimes it randomly does not update client store of useSession. Because of that, components that depend on that hook are not re-rendered.

Solution is to replace authClient.useSession() with custom hook useAuthSession():

import { authClient } from "@/lib/auth-client";
import { useEffect, useRef } from "react";

function useAuthSession() {
  const {
    data,
    isPending,
    error, //error object
    refetch,
  } = authClient.useSession();

  // Track the latest value of isPending
  const isPendingRef = useRef(isPending);

  useEffect(() => {
    isPendingRef.current = isPending;
  }, [isPending]);

  useEffect(() => {
    if (isPending) {
      const timerNotify = setTimeout(() => {
        if (isPendingRef.current) {
          refetch(); // sends another get-session request to the server
          //authClient.$store.notify("$sessionSignal"); // or this poke store to trigger a re-render
        }
      }, 2500); // after 2.5 seconds of pending, force resetting client store
      const timerReload = setTimeout(() => {
        if (isPendingRef.current) {
          window.location.reload();
        }
      }, 5000); // after 5 seconds of pending, reload the page
      return () => {
        clearTimeout(timerNotify);
        clearTimeout(timerReload);
      };
    }
  }, [isPending]);

  return {
    data,
    isPending,
    error,
  };
}

export default useAuthSession;
<!-- gh-comment-id:2673147723 --> @JosipPardon commented on GitHub (Feb 21, 2025): I have solution. When component with `useSession` is mounted, it always fetches session with `api/auth/get-session` request, but sometimes it randomly does not update client store of `useSession.` Because of that, components that depend on that hook are not re-rendered. Solution is to replace `authClient.useSession()` with custom hook `useAuthSession()`: ```tsx import { authClient } from "@/lib/auth-client"; import { useEffect, useRef } from "react"; function useAuthSession() { const { data, isPending, error, //error object refetch, } = authClient.useSession(); // Track the latest value of isPending const isPendingRef = useRef(isPending); useEffect(() => { isPendingRef.current = isPending; }, [isPending]); useEffect(() => { if (isPending) { const timerNotify = setTimeout(() => { if (isPendingRef.current) { refetch(); // sends another get-session request to the server //authClient.$store.notify("$sessionSignal"); // or this poke store to trigger a re-render } }, 2500); // after 2.5 seconds of pending, force resetting client store const timerReload = setTimeout(() => { if (isPendingRef.current) { window.location.reload(); } }, 5000); // after 5 seconds of pending, reload the page return () => { clearTimeout(timerNotify); clearTimeout(timerReload); }; } }, [isPending]); return { data, isPending, error, }; } export default useAuthSession; ```
Author
Owner

@joseph-9900 commented on GitHub (Feb 26, 2025):

Facing the same issue! @kedom1337 have you found a solution?

<!-- gh-comment-id:2683831182 --> @joseph-9900 commented on GitHub (Feb 26, 2025): Facing the same issue! @kedom1337 have you found a solution?
Author
Owner

@Bekacru commented on GitHub (Feb 27, 2025):

Hey everyone, sorry for the late reply. I still can’t reproduce the issue. Could someone provide a reproduction or at least share a screen recording of the code and the behavior?

Here’s what I just tried, everything is happening on the client. after the user signed in isPending get triggered and shows the loading icon, and when pending is false and the session is fetched it displays the session.

what i'm essentially doing:

function SessionComp() {
    const { data, isPending, error } = client.useSession();
    return (
        <div>
            {isPending ? (
                <div>
                    <Loader2 />
                </div>
            ) : error ? (
                <div>
                    Error: {error.message}
                </div>
            ) : session ? (
                <div>
                 {/* display the session */}
               </div>
            ) : (
                <div> </div>
            )}
        </div>
    );
}

https://github.com/user-attachments/assets/349eb5c3-c8f9-42e8-8d1b-57adb8cefa5f

<!-- gh-comment-id:2687074198 --> @Bekacru commented on GitHub (Feb 27, 2025): Hey everyone, sorry for the late reply. I still can’t reproduce the issue. Could someone provide a reproduction or at least share a screen recording of the code and the behavior? Here’s what I just tried, everything is happening on the client. after the user signed in `isPending` get triggered and shows the loading icon, and when pending is false and the session is fetched it displays the session. what i'm essentially doing: ```tsx function SessionComp() { const { data, isPending, error } = client.useSession(); return ( <div> {isPending ? ( <div> <Loader2 /> </div> ) : error ? ( <div> Error: {error.message} </div> ) : session ? ( <div> {/* display the session */} </div> ) : ( <div> </div> )} </div> ); } ``` https://github.com/user-attachments/assets/349eb5c3-c8f9-42e8-8d1b-57adb8cefa5f
Author
Owner

@AkshatKejriwal commented on GitHub (Feb 27, 2025):

Hey everyone, sorry for the late reply. I still can’t reproduce the issue. Could someone provide a reproduction or at least share a screen recording of the code and the behavior?

Here’s what I just tried, everything is happening on the client. after the user signed in isPending get triggered and shows the loading icon, and when pending is false and the session is fetched it displays the session.

what i'm essentially doing:

function SessionComp() {
const { data, isPending, error } = client.useSession();
return (


{isPending ? (



) : error ? (

Error: {error.message}

) : session ? (

{/* display the session */}

) : (

)}

);
}

Screen.Recording.2025-02-27.at.9.47.00.AM.mov

So, this is in a navbar and the ProfileDropdown has a logout button. Clicking the logout button doesn't update the state in nav and it keeps showing the ProfileDropdown.

Nav:

  const { data: session, isPending } = useSession();
  const user = session?.user as User;
  {
    !session?.user ? (
      <div className="hidden items-center gap-2 lg:flex">
        <LoginButton disabled={isPending} variant="outline">
          {tCommon("buttons.login")}
        </LoginButton>
        <RegisterButton disabled={isPending}>
          {tCommon("buttons.register")}
        </RegisterButton>
      </div>
    ) : (
      <div className="hidden items-center gap-2 lg:flex">
        <Link href="/event/create">
          <Button>{tCommon("buttons.createEvent")}</Button>
        </Link>
        <ProfileDropdown user={user} />
      </div>
    );
  }

Here's the logout button:

    try {
      setPending(true);
      await authClient.signOut({
        fetchOptions: {
          onSuccess: () => {
            toast({
              title: "Logged out",
              description: "You have been logged out",
            });
            // window.location.reload(); // TODO: temporary fix for the session not updating in the navbar
          },
        },
      });
    } catch (error) {
      console.error("Error signing out:", error);
    } finally {
      setPending(false);
    }
  };
<!-- gh-comment-id:2688906605 --> @AkshatKejriwal commented on GitHub (Feb 27, 2025): > Hey everyone, sorry for the late reply. I still can’t reproduce the issue. Could someone provide a reproduction or at least share a screen recording of the code and the behavior? > > Here’s what I just tried, everything is happening on the client. after the user signed in `isPending` get triggered and shows the loading icon, and when pending is false and the session is fetched it displays the session. > > what i'm essentially doing: > > function SessionComp() { > const { data, isPending, error } = client.useSession(); > return ( > <div> > {isPending ? ( > <div> > <Loader2 /> > </div> > ) : error ? ( > <div> > Error: {error.message} > </div> > ) : session ? ( > <div> > {/* display the session */} > </div> > ) : ( > <div> </div> > )} > </div> > ); > } > > Screen.Recording.2025-02-27.at.9.47.00.AM.mov So, this is in a navbar and the ProfileDropdown has a logout button. Clicking the logout button doesn't update the state in nav and it keeps showing the ProfileDropdown. Nav: ```jsx const { data: session, isPending } = useSession(); const user = session?.user as User; { !session?.user ? ( <div className="hidden items-center gap-2 lg:flex"> <LoginButton disabled={isPending} variant="outline"> {tCommon("buttons.login")} </LoginButton> <RegisterButton disabled={isPending}> {tCommon("buttons.register")} </RegisterButton> </div> ) : ( <div className="hidden items-center gap-2 lg:flex"> <Link href="/event/create"> <Button>{tCommon("buttons.createEvent")}</Button> </Link> <ProfileDropdown user={user} /> </div> ); } ``` Here's the logout button: ```jsx const handleLogout = async () => { try { setPending(true); await authClient.signOut({ fetchOptions: { onSuccess: () => { toast({ title: "Logged out", description: "You have been logged out", }); // window.location.reload(); // TODO: temporary fix for the session not updating in the navbar }, }, }); } catch (error) { console.error("Error signing out:", error); } finally { setPending(false); } }; ```
Author
Owner

@amitsharma1511 commented on GitHub (Mar 3, 2025):

What I am able to deduce is, the issue is specific to react 19.

To reproduce below versions are required -

"next": "^15.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",

Attaching video recording for reference -

https://github.com/user-attachments/assets/b99ae098-9683-4957-af12-e1166091fee0

<!-- gh-comment-id:2693306598 --> @amitsharma1511 commented on GitHub (Mar 3, 2025): What I am able to deduce is, the issue is specific to react 19. To reproduce below versions are required - "next": "^15.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", Attaching video recording for reference - https://github.com/user-attachments/assets/b99ae098-9683-4957-af12-e1166091fee0
Author
Owner

@developedbyed commented on GitHub (Mar 4, 2025):

Encountered the same thing, must be related to React 19

<!-- gh-comment-id:2698044010 --> @developedbyed commented on GitHub (Mar 4, 2025): Encountered the same thing, must be related to React 19
Author
Owner

@joseph-9900 commented on GitHub (Mar 4, 2025):

Hey @Bekacru Please check the issue 🙏

<!-- gh-comment-id:2698271762 --> @joseph-9900 commented on GitHub (Mar 4, 2025): Hey @Bekacru Please check the issue 🙏
Author
Owner

@Bekacru commented on GitHub (Mar 4, 2025):

Hey everyone, I still can't seem to reproduce it on my end. Any reproduction code would be great.
You can check the demo here, it's on React 19:
https://github.com/better-auth/better-auth/blob/main/demo/nextjs/package.json#L75

I’ve also added a client-only sign-in/use-session demo here:
https://demo.better-auth.com/client-test

<!-- gh-comment-id:2698305989 --> @Bekacru commented on GitHub (Mar 4, 2025): Hey everyone, I still can't seem to reproduce it on my end. Any reproduction code would be great. You can check the demo here, it's on React 19: https://github.com/better-auth/better-auth/blob/main/demo/nextjs/package.json#L75 I’ve also added a client-only sign-in/use-session demo here: https://demo.better-auth.com/client-test
Author
Owner

@lucasaraujonrt commented on GitHub (Mar 5, 2025):

Hey everyone, I still can't seem to reproduce it on my end. Any reproduction code would be great. You can check the demo here, it's on React 19: https://github.com/better-auth/better-auth/blob/main/demo/nextjs/package.json#L75

I’ve also added a client-only sign-in/use-session demo here: https://demo.better-auth.com/client-test

Hey @Bekacru I also tried to reproduce here and I believe I found the problem, if the page is server-side and the data is passed by props to the child, and if the child component is the client hook, the data is not updated.

ex:

Server component

import Content from "@/app/profile/(components)/content";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function Page() {
  const [session] = await Promise.all([
    auth.api.getSession({
      headers: await headers(),
    }),
  ]).catch(() => {
    throw redirect("/login");
  });

  return <Content session={session} />;
}

Client component:

const { refetch } = authClient.useSession();

await authClient.updateUser(
    {
      image: await convertImageToBase64(logo),
    },
    {
      onSuccess: () => refetch(), // this not reload the values
    }
  );

I'm not sure if it's the same problem

<!-- gh-comment-id:2699603415 --> @lucasaraujonrt commented on GitHub (Mar 5, 2025): > Hey everyone, I still can't seem to reproduce it on my end. Any reproduction code would be great. You can check the demo here, it's on React 19: https://github.com/better-auth/better-auth/blob/main/demo/nextjs/package.json#L75 > > I’ve also added a client-only sign-in/use-session demo here: https://demo.better-auth.com/client-test Hey @Bekacru I also tried to reproduce here and I believe I found the problem, if the page is server-side and the data is passed by props to the child, and if the child component is the client hook, the data is not updated. ex: Server component ```tsx import Content from "@/app/profile/(components)/content"; import { auth } from "@/lib/auth"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; export default async function Page() { const [session] = await Promise.all([ auth.api.getSession({ headers: await headers(), }), ]).catch(() => { throw redirect("/login"); }); return <Content session={session} />; } ``` Client component: ```tsx const { refetch } = authClient.useSession(); await authClient.updateUser( { image: await convertImageToBase64(logo), }, { onSuccess: () => refetch(), // this not reload the values } ); ``` I'm not sure if it's the same problem
Author
Owner

@amitsharma1511 commented on GitHub (Mar 5, 2025):

Hi @lucasaraujonrt, you have got very close I think. The issue is when there is a mix and match of server and client component calls. I am signing in user using server component and server actions, but fetching the session on a client component.

export const signInAction = async (state: FormState, formData: FormData) => {
  const validatedFields = signInSchema.safeParse({
    email: formData.get("email"),
    password: formData.get("password"),
  });
  if (!validatedFields.success) {
    return { errors: validatedFields.error.flatten().fieldErrors };
  }
  const { email, password } = validatedFields.data;
  try {
    const result = await auth.api.signInEmail({
      body: {
        email,
        password,
      },
    });

    console.log("#### DATA ####:", result.user);
  } catch (error) {
    if (error instanceof APIError) {
      console.log("#### ERROR ####", error.message, error.status);
    }
  }

  redirect("/");
};
export const auth = betterAuth({
  baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_BASE_URL,
  database: drizzleAdapter(db, {
    provider: "sqlite", // or "mysql", "pg"
  }),
 // .......
  plugins: [nextCookies()], // make sure this is the last plugin in the array
});
"use client"
// .......
export function UserNav() {
  const { data: session, isPending, error, refetch } = authClient.useSession();
  console.log("USER NAV SESSION", session);

  return (
    <>
      {session?.session.id ? (
        <DropdownMenu> ) : (
        <Button asChild>
          <Link href="/signin">Sign In</Link>
        </Button>
      )}
    </>
  );
}
<!-- gh-comment-id:2699898434 --> @amitsharma1511 commented on GitHub (Mar 5, 2025): Hi @lucasaraujonrt, you have got very close I think. The issue is when there is a mix and match of server and client component calls. I am signing in user using server component and server actions, but fetching the session on a client component. ``` export const signInAction = async (state: FormState, formData: FormData) => { const validatedFields = signInSchema.safeParse({ email: formData.get("email"), password: formData.get("password"), }); if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors }; } const { email, password } = validatedFields.data; try { const result = await auth.api.signInEmail({ body: { email, password, }, }); console.log("#### DATA ####:", result.user); } catch (error) { if (error instanceof APIError) { console.log("#### ERROR ####", error.message, error.status); } } redirect("/"); }; ``` ``` export const auth = betterAuth({ baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_BASE_URL, database: drizzleAdapter(db, { provider: "sqlite", // or "mysql", "pg" }), // ....... plugins: [nextCookies()], // make sure this is the last plugin in the array }); ``` ``` "use client" // ....... export function UserNav() { const { data: session, isPending, error, refetch } = authClient.useSession(); console.log("USER NAV SESSION", session); return ( <> {session?.session.id ? ( <DropdownMenu> ) : ( <Button asChild> <Link href="/signin">Sign In</Link> </Button> )} </> ); } ```
Author
Owner

@joseph-9900 commented on GitHub (Mar 5, 2025):

Hey @amitsharma1511 @lucasaraujonrt if you all can create a repo in codesandbox or in github and share it here it would be useful from @Bekacru perspective. He is unable to reproduce the issue. But as so many people including myself is facing the issue that means the issue is still there.

<!-- gh-comment-id:2699934430 --> @joseph-9900 commented on GitHub (Mar 5, 2025): Hey @amitsharma1511 @lucasaraujonrt if you all can create a repo in codesandbox or in github and share it here it would be useful from @Bekacru perspective. He is unable to reproduce the issue. But as so many people including myself is facing the issue that means the issue is still there.
Author
Owner

@raburuz commented on GitHub (Mar 5, 2025):

This issue happens with React 19.
I have the same exact code in my app with React 18 and everything works perfectly, but when I update to React 19 this hook doesn't work anymore.

My stack is React with vitejs and nodejs with express

https://www.reddit.com/r/better_auth/s/eCIAQK0luS

<!-- gh-comment-id:2701257309 --> @raburuz commented on GitHub (Mar 5, 2025): This issue happens with React 19. I have the same exact code in my app with React 18 and everything works perfectly, but when I update to React 19 this hook doesn't work anymore. My stack is React with vitejs and nodejs with express https://www.reddit.com/r/better_auth/s/eCIAQK0luS
Author
Owner

@natew commented on GitHub (Mar 6, 2025):

I'm hitting this when logging out just doing this:

Image

So not React 19, it never calls this when you log out.

<!-- gh-comment-id:2703076121 --> @natew commented on GitHub (Mar 6, 2025): I'm hitting this when logging out just doing this: ![Image](https://github.com/user-attachments/assets/f6556bd6-ec85-48da-ae62-4e76b7a1b30c) So not React 19, it never calls this when you log out.
Author
Owner

@Rudrangg commented on GitHub (Mar 6, 2025):

Removing this seems to work?

session: { cookieCache: { enabled: true, maxAge: 60, }, },

<!-- gh-comment-id:2704251343 --> @Rudrangg commented on GitHub (Mar 6, 2025): Removing this seems to work? ` session: { cookieCache: { enabled: true, maxAge: 60, }, },`
Author
Owner

@joseph-9900 commented on GitHub (Mar 6, 2025):

Another issue is after google OAuth login it still can't retrieve session and asks for login again. It's really frustrating when you think of this in a production environment. Will drop better-auth for now and check later if they fix this thing up. It's a very good library but with this issue can't deploy app on production!

<!-- gh-comment-id:2704674075 --> @joseph-9900 commented on GitHub (Mar 6, 2025): Another issue is after google OAuth login it still can't retrieve session and asks for login again. It's really frustrating when you think of this in a production environment. Will drop better-auth for now and check later if they fix this thing up. It's a very good library but with this issue can't deploy app on production!
Author
Owner

@ChatterjeeArka commented on GitHub (Mar 8, 2025):

Hey @Bekacru, facing the same issue. Is there any solution out now?

<!-- gh-comment-id:2707988812 --> @ChatterjeeArka commented on GitHub (Mar 8, 2025): Hey @Bekacru, facing the same issue. Is there any solution out now?
Author
Owner

@asimaranov commented on GitHub (Mar 11, 2025):

Facing same issue, get-session request succeeds but data is empty sometimes. Only force refetching made things work

<!-- gh-comment-id:2712705842 --> @asimaranov commented on GitHub (Mar 11, 2025): Facing same issue, get-session request succeeds but data is empty sometimes. Only force refetching made things work
Author
Owner

@lucasaraujonrt commented on GitHub (Mar 12, 2025):

Hi guys, i fixed my problem using the useRoute hook from next/navigation, replacing the refetch from authClient

In my case, the session is passed by props to children component, so refresh work's perfectly

Client component

const router = useRoute();

await authClient.updateUser(
  {
    image: await convertImageToBase64(logo),
  },
  {
    onSuccess: () => router.refresh(), // get the new session values
  }
);

Server component

import Content from "@/app/profile/(components)/content";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function Page() {
  const [session] = await Promise.all([
    auth.api.getSession({
      headers: await headers(),
    }),
  ]).catch(() => {
    throw redirect("/login");
  });

  return <Content session={session} />;
}
<!-- gh-comment-id:2716262179 --> @lucasaraujonrt commented on GitHub (Mar 12, 2025): Hi guys, i fixed my problem using the `useRoute` hook from `next/navigation`, replacing the refetch from `authClient` In my case, the session is passed by props to children component, so refresh work's perfectly Client component ```ts const router = useRoute(); await authClient.updateUser( { image: await convertImageToBase64(logo), }, { onSuccess: () => router.refresh(), // get the new session values } ); ``` Server component ```tsx import Content from "@/app/profile/(components)/content"; import { auth } from "@/lib/auth"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; export default async function Page() { const [session] = await Promise.all([ auth.api.getSession({ headers: await headers(), }), ]).catch(() => { throw redirect("/login"); }); return <Content session={session} />; } ```
Author
Owner

@Makakusnik commented on GitHub (Mar 12, 2025):

+1 Aswell on 1.2.2..

Refresh does work but refreshing whole page removes bunch of state, which should not be removed just by signing in..

<!-- gh-comment-id:2718142801 --> @Makakusnik commented on GitHub (Mar 12, 2025): +1 Aswell on 1.2.2.. Refresh does work but refreshing whole page removes bunch of state, which should not be removed just by signing in..
Author
Owner

@lukasb commented on GitHub (Mar 13, 2025):

+1 on 1.2.3

<!-- gh-comment-id:2719667991 --> @lukasb commented on GitHub (Mar 13, 2025): +1 on 1.2.3
Author
Owner

@joseph-9900 commented on GitHub (Mar 13, 2025):

The session management issue in pending state is a head scratcher. I moved to clerk for my production project. Hey @Bekacru so many people are facing the same issue, I guess you should seriously see this rather focusing on new features like stripe and all! If the basic feature is only buggy then what is the use of using this.

<!-- gh-comment-id:2719956675 --> @joseph-9900 commented on GitHub (Mar 13, 2025): The session management issue in pending state is a head scratcher. I moved to clerk for my production project. Hey @Bekacru so many people are facing the same issue, I guess you should seriously see this rather focusing on new features like stripe and all! If the basic feature is only buggy then what is the use of using this.
Author
Owner

@jtj60 commented on GitHub (Mar 16, 2025):

Is there any plan to fix this? I've loved the library so far but this is gonna force me to switch to something else if not addressed soon...

<!-- gh-comment-id:2727685391 --> @jtj60 commented on GitHub (Mar 16, 2025): Is there any plan to fix this? I've loved the library so far but this is gonna force me to switch to something else if not addressed soon...
Author
Owner

@asimaranov commented on GitHub (Mar 17, 2025):

@Bekacru, could you check please?

<!-- gh-comment-id:2728272531 --> @asimaranov commented on GitHub (Mar 17, 2025): @Bekacru, could you check please?
Author
Owner

@Bekacru commented on GitHub (Mar 17, 2025):

Hey everyone, could you please confirm me if this is fixed on 1.2.5-beta.4?

<!-- gh-comment-id:2730607260 --> @Bekacru commented on GitHub (Mar 17, 2025): Hey everyone, could you please confirm me if this is fixed on `1.2.5-beta.4`?
Author
Owner

@Rudrangg commented on GitHub (Mar 18, 2025):

Hey everyone, could you please confirm me if this is fixed on 1.2.5-beta.4?

It is working fine for me! Thank you!!

<!-- gh-comment-id:2731987371 --> @Rudrangg commented on GitHub (Mar 18, 2025): > Hey everyone, could you please confirm me if this is fixed on `1.2.5-beta.4`? It is working fine for me! _Thank you!!_
Author
Owner

@Bekacru commented on GitHub (Mar 21, 2025):

Hey everyone, just figured out the root issue here after someone sent me a poc. It's due to object reference and destructuring. If you try to use a destructured object from the auth client, you need to use the destructured useSession instead of authClient.useSession and vice versa.

const authClient = createAuthClient();
const { signIn, useSession } = authClient;

// Method 1: Using destructured methods - This works
signIn(...);
useSession();
// When you use both destructured methods together, the session updates properly


// Method 2: Using direct object methods - This works too
authClient.signIn(...);
authClient.useSession();
// When you use both direct object methods together, the session updates properly


// Method 3: Mixing styles - This breaks! :x:
authClient.signIn(...);
useSession();
// When you mix direct object access with destructured methods, it breaks because it disrupts the object reference needed to trigger atoms.

We'll try to investigate on how we potentially support them interchangeably. But we'll document this in the meantime

<!-- gh-comment-id:2742622552 --> @Bekacru commented on GitHub (Mar 21, 2025): Hey everyone, just figured out the root issue here after someone sent me a poc. It's due to object reference and destructuring. If you try to use a destructured object from the auth client, you need to use the destructured useSession instead of authClient.useSession and vice versa. ```ts const authClient = createAuthClient(); const { signIn, useSession } = authClient; // Method 1: Using destructured methods - This works signIn(...); useSession(); // When you use both destructured methods together, the session updates properly // Method 2: Using direct object methods - This works too authClient.signIn(...); authClient.useSession(); // When you use both direct object methods together, the session updates properly // Method 3: Mixing styles - This breaks! :x: authClient.signIn(...); useSession(); // When you mix direct object access with destructured methods, it breaks because it disrupts the object reference needed to trigger atoms. ``` We'll try to investigate on how we potentially support them interchangeably. But we'll document this in the meantime
Author
Owner

@ChatterjeeArka commented on GitHub (Mar 21, 2025):

Hey everyone, just figured out the root issue here after someone sent me a poc. It's due to object reference and destructuring. If you try to use a destructured object from the auth client, you need to use the destructured useSession instead of authClient.useSession and vice versa.

const authClient = createAuthClient();
const { signIn, useSession } = authClient;

// Method 1: Using destructured methods - This works
signIn(...);
useSession();
// When you use both destructured methods together, the session updates properly

// Method 2: Using direct object methods - This works too
authClient.signIn(...);
authClient.useSession();
// When you use both direct object methods together, the session updates properly

// Method 3: Mixing styles - This breaks!
authClient.signIn(...);
useSession();
// When you mix direct object access with destructured methods, it breaks because it disrupts the object reference needed to trigger atoms.
We'll try to investigate on how we potentially support them interchangeably. But we'll document this in the meantime

Hey @Bekacru you are correct, I just checked my old code which was giving error on github before using custom session hook and I was using this code

For getting session

const {
        data: session,
        isPending, //loading state
    } = authClient.useSession();

    console.log("session: ", session);

And for login with google

<Button
   variant="outline"
    className="flex items-center gap-2"
    onClick={async () => {
    setSignInClicked(true);
    toast.promise(
       signIn.social({
            provider: "google",
             callbackURL: "/",
        }),
       {
          loading: "Redirecting...",
           success: "Redirected successfully!",
            error: "Login failed",
         },
      );
     }}
>
   <GoogleLogo /> Sign in with Google

</Button>
<!-- gh-comment-id:2742688048 --> @ChatterjeeArka commented on GitHub (Mar 21, 2025): > Hey everyone, just figured out the root issue here after someone sent me a poc. It's due to object reference and destructuring. If you try to use a destructured object from the auth client, you need to use the destructured useSession instead of authClient.useSession and vice versa. > > const authClient = createAuthClient(); > const { signIn, useSession } = authClient; > > // Method 1: Using destructured methods - This works > signIn(...); > useSession(); > // When you use both destructured methods together, the session updates properly > > > // Method 2: Using direct object methods - This works too > authClient.signIn(...); > authClient.useSession(); > // When you use both direct object methods together, the session updates properly > > > // Method 3: Mixing styles - This breaks! :x: > authClient.signIn(...); > useSession(); > // When you mix direct object access with destructured methods, it breaks because it disrupts the object reference needed to trigger atoms. > We'll try to investigate on how we potentially support them interchangeably. But we'll document this in the meantime Hey @Bekacru you are correct, I just checked my old code which was giving error on github before using custom session hook and I was using this code For getting session ``` const { data: session, isPending, //loading state } = authClient.useSession(); console.log("session: ", session); ``` And for login with google ``` <Button variant="outline" className="flex items-center gap-2" onClick={async () => { setSignInClicked(true); toast.promise( signIn.social({ provider: "google", callbackURL: "/", }), { loading: "Redirecting...", success: "Redirected successfully!", error: "Login failed", }, ); }} > <GoogleLogo /> Sign in with Google </Button> ```
Author
Owner

@kawpii commented on GitHub (Mar 21, 2025):

I do not know how you guys use useSession it just throws me a type error

Image

Edit:

Just closing and opening VS Code error disappeared not I'm getting two more errors

Image

It seems like Better Auth only support SSR.

<!-- gh-comment-id:2743085828 --> @kawpii commented on GitHub (Mar 21, 2025): I do not know how you guys use `useSession` it just throws me a type error ![Image](https://github.com/user-attachments/assets/10ff5925-463a-4c6b-94e1-b4882024f251) Edit: Just closing and opening VS Code error disappeared not I'm getting two more errors ![Image](https://github.com/user-attachments/assets/02dc8dd5-9786-4979-afc6-6352e3c63923) It seems like Better Auth only support SSR.
Author
Owner

@Rudrangg commented on GitHub (Mar 21, 2025):

Edit:

Just closing and opening VS Code error disappeared not I'm getting two more errors

Image

It seems like Better Auth only support SSR.

You cannot use await in a client component...
Correct usage

<!-- gh-comment-id:2743135499 --> @Rudrangg commented on GitHub (Mar 21, 2025): > Edit: > > Just closing and opening VS Code error disappeared not I'm getting two more errors > > ![Image](https://github.com/user-attachments/assets/02dc8dd5-9786-4979-afc6-6352e3c63923) > > It seems like Better Auth only support SSR. You cannot use await in a client component... [Correct usage](https://www.better-auth.com/docs/concepts/session-management#use-session)
Author
Owner

@kawpii commented on GitHub (Mar 21, 2025):

Edit:
Just closing and opening VS Code error disappeared not I'm getting two more errors
Image
It seems like Better Auth only support SSR.

You cannot use await in a client component... Correct usage

Thanks, I just forgot to remove it.

Edit:

Still could not figure out why the useSession hook gives a type error.

<!-- gh-comment-id:2743294145 --> @kawpii commented on GitHub (Mar 21, 2025): > > Edit: > > Just closing and opening VS Code error disappeared not I'm getting two more errors > > ![Image](https://github.com/user-attachments/assets/02dc8dd5-9786-4979-afc6-6352e3c63923) > > It seems like Better Auth only support SSR. > > You cannot use await in a client component... [Correct usage](https://www.better-auth.com/docs/concepts/session-management#use-session) Thanks, I just forgot to remove it. Edit: Still could not figure out why the `useSession` hook gives a type error.
Author
Owner

@xaliz-06 commented on GitHub (Mar 23, 2025):

Hey guys, I'm facing the same issue here. I tried to go to 1.2.5-beta.4 but that returns null too. Anyone got a fix?

"use client";

import React from "react";
import { Button } from "@/components/ui/button";
import { ModeToggle } from "@/components/mode-toggle";
import { useRouter } from "next/navigation";
import { authClient } from "@/lib/auth-client";

const Navbar = () => {
  const navigation = useRouter();
  const {
    data: session,
    isPending, //loading state
    error, //error object
    refetch, //refetch the session
  } = authClient.useSession();

  console.log("Error: ", error);
  console.log("Session: ", session);

  const handleCreateAccount = () => {
    navigation.push("/auth/sign-up");
  };
// ...

I have not destructed the client object also. I wonder why client-side session fetching doesn't work. I can see that a call to api/auth/get-session is made that returns with a status code 200 (but no response or nothing in the request body for that matter)

<!-- gh-comment-id:2746138800 --> @xaliz-06 commented on GitHub (Mar 23, 2025): Hey guys, I'm facing the same issue here. I tried to go to `1.2.5-beta.4` but that returns null too. Anyone got a fix? ```js "use client"; import React from "react"; import { Button } from "@/components/ui/button"; import { ModeToggle } from "@/components/mode-toggle"; import { useRouter } from "next/navigation"; import { authClient } from "@/lib/auth-client"; const Navbar = () => { const navigation = useRouter(); const { data: session, isPending, //loading state error, //error object refetch, //refetch the session } = authClient.useSession(); console.log("Error: ", error); console.log("Session: ", session); const handleCreateAccount = () => { navigation.push("/auth/sign-up"); }; // ... ``` I have not destructed the client object also. I wonder why client-side session fetching doesn't work. I can see that a call to `api/auth/get-session` is made that returns with a status code 200 (but no response or nothing in the request body for that matter)
Author
Owner

@Bekacru commented on GitHub (Mar 23, 2025):

Still could not figure out why the useSession hook gives a type error.

@kaushalyap make sure you're importing createAuthClient from "/react" not "/client"

<!-- gh-comment-id:2746194845 --> @Bekacru commented on GitHub (Mar 23, 2025): > Still could not figure out why the `useSession` hook gives a type error. @kaushalyap make sure you're importing `createAuthClient` from "/react" not "/client"
Author
Owner

@Bekacru commented on GitHub (Mar 23, 2025):

Hey guys, I'm facing the same issue here. I tried to go to 1.2.5-beta.4 but that returns null too. Anyone got a fix?

@xaliz-06 this isn't related to to this issue. getSession could return null for differnt reasons. But for a start make sure there is a cookie in your browser

<!-- gh-comment-id:2746195981 --> @Bekacru commented on GitHub (Mar 23, 2025): > Hey guys, I'm facing the same issue here. I tried to go to `1.2.5-beta.4` but that returns null too. Anyone got a fix? @xaliz-06 this isn't related to to this issue. `getSession` could return null for differnt reasons. But for a start make sure there is a cookie in your browser
Author
Owner

@derek-rein commented on GitHub (Apr 1, 2025):

A hack fix:

// Define session data type
interface SessionData {
    user?: {
        id?: string;
        email?: string;
        name?: string;
    };
    expires?: string;
}

// Custom hook for safely fetching session data
export function useSafeSession() {
    const [sessionData, setSessionData] = useState<SessionData | null>(null);
    const [isLoading, setIsLoading] = useState(true);
    const [error, setError] = useState<Error | null>(null);

    useEffect(() => {
        const fetchSession = async () => {
            try {
                setIsLoading(true);
                const { data, error: sessionError } = await authClient.getSession();
                
                if (sessionError) {
                    setError(new Error(sessionError.message));
                } else {
                    setSessionData(data);
                }
            } catch (err) {
                setError(err instanceof Error ? err : new Error('Failed to fetch session'));
                console.error("Error fetching session:", err);
            } finally {
                setIsLoading(false);
            }
        };

        fetchSession();
    }, []);

    // Method to refresh the session data
    const refresh = async () => {
        try {
            setIsLoading(true);
            const { data, error: sessionError } = await authClient.getSession();
            
            if (sessionError) {
                setError(new Error(sessionError.message));
            } else {
                setSessionData(data);
            }
        } catch (err) {
            setError(err instanceof Error ? err : new Error('Failed to fetch session'));
            console.error("Error refreshing session:", err);
        } finally {
            setIsLoading(false);
        }
    };

    return {
        data: sessionData,
        isLoading,
        error,
        refresh
    };
}

I was previously getting this when using the supplied better-auth/react hook.

TypeError: Cannot read properties of null (reading 'useRef')
    at useRef2 (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:1075:29)
    at useStore (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:3127:45)
    at resolvedHooks.<computed> (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:3161:44)
    at UserProfile (http://localhost:5173/src/components/UserProfile.tsx?t=1743525472584:21:10)
    at react-stack-bottom-frame (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:17424:20)
    at renderWithHooks (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:4206:24)
    at updateFunctionComponent (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:6619:21)
    at beginWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:7654:20)
    at runWithFiberInDEV (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:1485:72)
    at performUnitOfWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:10868:98)
<!-- gh-comment-id:2770000185 --> @derek-rein commented on GitHub (Apr 1, 2025): A hack fix: ``` // Define session data type interface SessionData { user?: { id?: string; email?: string; name?: string; }; expires?: string; } // Custom hook for safely fetching session data export function useSafeSession() { const [sessionData, setSessionData] = useState<SessionData | null>(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { const fetchSession = async () => { try { setIsLoading(true); const { data, error: sessionError } = await authClient.getSession(); if (sessionError) { setError(new Error(sessionError.message)); } else { setSessionData(data); } } catch (err) { setError(err instanceof Error ? err : new Error('Failed to fetch session')); console.error("Error fetching session:", err); } finally { setIsLoading(false); } }; fetchSession(); }, []); // Method to refresh the session data const refresh = async () => { try { setIsLoading(true); const { data, error: sessionError } = await authClient.getSession(); if (sessionError) { setError(new Error(sessionError.message)); } else { setSessionData(data); } } catch (err) { setError(err instanceof Error ? err : new Error('Failed to fetch session')); console.error("Error refreshing session:", err); } finally { setIsLoading(false); } }; return { data: sessionData, isLoading, error, refresh }; } ``` I was previously getting this when using the supplied better-auth/react hook. ``` TypeError: Cannot read properties of null (reading 'useRef') at useRef2 (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:1075:29) at useStore (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:3127:45) at resolvedHooks.<computed> (http://localhost:5173/node_modules/.vite/deps/better-auth_react.js?v=5bb48250:3161:44) at UserProfile (http://localhost:5173/src/components/UserProfile.tsx?t=1743525472584:21:10) at react-stack-bottom-frame (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:17424:20) at renderWithHooks (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:4206:24) at updateFunctionComponent (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:6619:21) at beginWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:7654:20) at runWithFiberInDEV (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:1485:72) at performUnitOfWork (http://localhost:5173/node_modules/.vite/deps/react-dom_client.js?v=5bb48250:10868:98) ```
Author
Owner

@cadinhoruf commented on GitHub (Apr 25, 2025):

+1 to this, also the expo adapter is not redirecting the users after signin wih social

you need to put hook config at auth.ts

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { db } from "./prisma";
import { createAuthMiddleware } from "better-auth/api";

export const auth = betterAuth({
database: prismaAdapter(db, {
provider: "postgresql",
}),
socialProviders:{
github:{
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
},
},
session:{
cookieCache: {
enabled: true,
maxAge: 5 * 60
},
},
hooks:{
after: createAuthMiddleware(async (ctx) => {
if(ctx.path.startsWith("/sign-up")){
const newSession = ctx.context.newSession;
if(newSession){
throw ctx.redirect(/dashboard)
}
}
}),
}

});

export type Session = typeof auth.$Infer.Session

<!-- gh-comment-id:2830954537 --> @cadinhoruf commented on GitHub (Apr 25, 2025): > +1 to this, also the expo adapter is not redirecting the users after signin wih social you need to put hook config at auth.ts import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { db } from "./prisma"; import { createAuthMiddleware } from "better-auth/api"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), socialProviders:{ github:{ clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, }, }, session:{ cookieCache: { enabled: true, maxAge: 5 * 60 }, }, **hooks:{ after: createAuthMiddleware(async (ctx) => { if(ctx.path.startsWith("/sign-up")){ const newSession = ctx.context.newSession; if(newSession){ throw ctx.redirect(`/dashboard`) } } }), }** }); export type Session = typeof auth.$Infer.Session
Author
Owner

@widavies commented on GitHub (May 4, 2025):

@Bekacru
I've written a short utility function that makes use of React's suspense boundaries so that I don't have to check isPending in all of my components. I see this issue happen frequently.

  • I don't destructure authClient anywhere
  • I import from React: import { createAuthClient } from 'better-auth/react';
  • I'm on Next canary 15.4.0-canary.19 better-auth 1.2.7
  • Both cookies are present better-auth.session_data and better-auth.session_token
  • I name my function useSession - does this matter? (the issue is still present if I rename it useAuthSession, etc.)

I can provide a repo to reproduce this issue if necessary.

function useSession(options: { or: 'redirect' | 'throw' }): {
  session: Session;
  refetch: () => void;
};
function useSession(options?: { or?: never }): {
  session: Session;
  refetch: () => void;
};

function useSession(options?: {
  or?: 'redirect' | 'throw';
}): { session: Session; refetch: () => void } | null {
  suspendIfSsr('useSession');

  const router = useRouter();
  const { data, error, isPending, refetch } = authClient.useSession();

  // isPending=true is stuck! Never makes it past this if statement
  if (isPending) {
    suspend();
  }

  if (error) throw error;

  if (data === null) {
    switch (options?.or) {
      case 'redirect':
        router.push('/auth/signin');
        suspend();
        throw new Error('User is not signed in');
      case 'throw':
      default:
        throw new Error('User is not signed in');
    }
  } else {
    return {
      session: data,
      refetch,
    };
  }
}

// Helper functions

export function isBrowserLike() {
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined' &&
    typeof document.createElement !== 'undefined'
  );
}

export function suspendIfSsr(caller?: string) {
  if (!isBrowserLike()) {
    throw new NoSuspenseBoundaryError({ caller });
  }
}

export function suspend(): never {
  // This will never resolve!
  throw new Promise(() => {});
}


<!-- gh-comment-id:2849445125 --> @widavies commented on GitHub (May 4, 2025): @Bekacru I've written a short utility function that makes use of React's suspense boundaries so that I don't have to check isPending in all of my components. I see this issue happen frequently. - I don't destructure `authClient` anywhere - I import from React: `import { createAuthClient } from 'better-auth/react';` - I'm on Next canary `15.4.0-canary.19` better-auth `1.2.7` - Both cookies are present `better-auth.session_data` and `better-auth.session_token` - I name my function `useSession` - does this matter? (the issue is still present if I rename it `useAuthSession`, etc.) I can provide a repo to reproduce this issue if necessary. ```ts function useSession(options: { or: 'redirect' | 'throw' }): { session: Session; refetch: () => void; }; function useSession(options?: { or?: never }): { session: Session; refetch: () => void; }; function useSession(options?: { or?: 'redirect' | 'throw'; }): { session: Session; refetch: () => void } | null { suspendIfSsr('useSession'); const router = useRouter(); const { data, error, isPending, refetch } = authClient.useSession(); // isPending=true is stuck! Never makes it past this if statement if (isPending) { suspend(); } if (error) throw error; if (data === null) { switch (options?.or) { case 'redirect': router.push('/auth/signin'); suspend(); throw new Error('User is not signed in'); case 'throw': default: throw new Error('User is not signed in'); } } else { return { session: data, refetch, }; } } // Helper functions export function isBrowserLike() { return ( typeof window !== 'undefined' && typeof document !== 'undefined' && typeof document.createElement !== 'undefined' ); } export function suspendIfSsr(caller?: string) { if (!isBrowserLike()) { throw new NoSuspenseBoundaryError({ caller }); } } export function suspend(): never { // This will never resolve! throw new Promise(() => {}); } ```
Author
Owner

@faca69 commented on GitHub (May 14, 2025):

any news on this?

<!-- gh-comment-id:2881446381 --> @faca69 commented on GitHub (May 14, 2025): any news on this?
Author
Owner

@Muhammed-Rahif commented on GitHub (May 25, 2025):

A quick workaround I’m using, a component that refetches the session when the pathname changes, in case useSession() doesn’t update as expected.

src\components\session-updater.tsx:

"use client";

import { useEffect } from "react";
import { usePathname } from "next/navigation";

import { useSession } from "@/utils/client/auth-utils";

/**
 * This component is a temporary fix for the `useSession() not always triggering a state change` issue:
 * - https://github.com/better-auth/better-auth/issues/1006
 * - https://www.reddit.com/r/nextjs/comments/1jfnj59/help_betterauth_clientside_session_return_null/
 * 
 * This component should be removed once any of the above issues are resolved.
 */
export default function SessionUpdater() {
  const { data: session, refetch } = useSession();
  const pathname = usePathname();

  useEffect(() => {
    if (session) return;
    refetch(); // Refetch session data when the pathname changes
  }, [refetch, pathname, session]);
  return null;
}

Can be removed once the issue is resolved.

<!-- gh-comment-id:2907904663 --> @Muhammed-Rahif commented on GitHub (May 25, 2025): A quick workaround I’m using, a component that refetches the session when the pathname changes, in case `useSession()` doesn’t update as expected. `src\components\session-updater.tsx`: ```tsx "use client"; import { useEffect } from "react"; import { usePathname } from "next/navigation"; import { useSession } from "@/utils/client/auth-utils"; /** * This component is a temporary fix for the `useSession() not always triggering a state change` issue: * - https://github.com/better-auth/better-auth/issues/1006 * - https://www.reddit.com/r/nextjs/comments/1jfnj59/help_betterauth_clientside_session_return_null/ * * This component should be removed once any of the above issues are resolved. */ export default function SessionUpdater() { const { data: session, refetch } = useSession(); const pathname = usePathname(); useEffect(() => { if (session) return; refetch(); // Refetch session data when the pathname changes }, [refetch, pathname, session]); return null; } ``` Can be removed once the issue is resolved.
Author
Owner

@Nikola-Milovic commented on GitHub (Jun 2, 2025):

@Bekacru I can confirm that what you stated is not the case, at least for me.

import { createAuthClient } from "better-auth/react";

const authClient = createAuthClient({
  baseURL:"...",
  plugins: [],
});

export const {
  useSession,
  signIn,
  signUp,
  signOut,
  forgetPassword,
  resetPassword,
} = authClient;

export type Session = typeof authClient.$Infer.Session;
export type User = typeof authClient.$Infer.Session.user;

Only the destructed methods are used.

Latest version 1.28.0, this bug should probably be higher on the priority list considering how breaking it is

Here is how it manifests

isAuthenticated = !!session?.user <- we check if we have the session and the user

isAuthenticated false true <- first render is loading the session

session useEffect triggered {userId: null, location: '/'} <- initial effect trigger

No session or user id, not setting userId <- we have no session

isAuthenticated false true  <- we're still loading, another rerender

isAuthenticated true false <- we got the session, everythings working, but even though session changed use effect hasn't triggered

I am I missing something obvious or?

export function AuthProvider({ children }: { children: ReactNode }) {
  const { data, isPending } = useSession();
  const setUserId = useAppStore((state) => state.setUserId);

  const value = {
    session: data,
    isLoading: isPending,
    isAuthenticated: !!data?.user,
  };

  useEffect(() => {
    console.log('session useEffect triggered', {
      userId: data, location: location.pathname
    });

    if (data?.user?.id) {
      console.log('Setting userId in store:', data.user.id);
      setUserId(data.user.id);
    } else {
      console.log('No session or user id, not setting userId');
    }
  }, [data, setUserId]);

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
<!-- gh-comment-id:2929804606 --> @Nikola-Milovic commented on GitHub (Jun 2, 2025): @Bekacru I can confirm that what you stated is not the case, at least for me. ```typescript import { createAuthClient } from "better-auth/react"; const authClient = createAuthClient({ baseURL:"...", plugins: [], }); export const { useSession, signIn, signUp, signOut, forgetPassword, resetPassword, } = authClient; export type Session = typeof authClient.$Infer.Session; export type User = typeof authClient.$Infer.Session.user; ``` Only the destructed methods are used. Latest version `1.28.0`, this bug should probably be higher on the priority list considering how breaking it is Here is how it manifests ``` isAuthenticated = !!session?.user <- we check if we have the session and the user isAuthenticated false true <- first render is loading the session session useEffect triggered {userId: null, location: '/'} <- initial effect trigger No session or user id, not setting userId <- we have no session isAuthenticated false true <- we're still loading, another rerender isAuthenticated true false <- we got the session, everythings working, but even though session changed use effect hasn't triggered ``` I am I missing something obvious or? ```typescript export function AuthProvider({ children }: { children: ReactNode }) { const { data, isPending } = useSession(); const setUserId = useAppStore((state) => state.setUserId); const value = { session: data, isLoading: isPending, isAuthenticated: !!data?.user, }; useEffect(() => { console.log('session useEffect triggered', { userId: data, location: location.pathname }); if (data?.user?.id) { console.log('Setting userId in store:', data.user.id); setUserId(data.user.id); } else { console.log('No session or user id, not setting userId'); } }, [data, setUserId]); return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; } ```
Author
Owner

@iLambda commented on GitHub (Jun 17, 2025):

Can also confirm that it still happens to me that using the useSession() hook does not rerender some components properly.

I have a very basic setup with a component that mounts only if a session exists, and the user can disconnect if prompted by a dialog coming from somewhere else. If I unlog using the dialog, my other component never unmounts, unless I trigger a full page reload.

I've made sure that I've been using the latest versions, and I don't even export deconstructed methods from the authClient, so it's not that. The client is also coming from 'better-auth/react', so no problem there.

<!-- gh-comment-id:2978648642 --> @iLambda commented on GitHub (Jun 17, 2025): Can also confirm that it still happens to me that using the `useSession()` hook does not rerender some components properly. I have a very basic setup with a component that mounts only if a session exists, and the user can disconnect if prompted by a dialog coming from somewhere else. If I unlog using the dialog, my other component never unmounts, unless I trigger a full page reload. I've made sure that I've been using the latest versions, and I don't even export deconstructed methods from the authClient, so it's not that. The client is also coming from 'better-auth/react', so no problem there.
Author
Owner

@pranavp10 commented on GitHub (Jun 21, 2025):

check imports if you are using react then the import will be
import { createAuthClient } from "better-auth/react";

<!-- gh-comment-id:2993369340 --> @pranavp10 commented on GitHub (Jun 21, 2025): check imports if you are using react then the import will be `import { createAuthClient } from "better-auth/react"; `
Author
Owner

@wayne4o312 commented on GitHub (Jun 24, 2025):

the bug still exist

<!-- gh-comment-id:2998913476 --> @wayne4o312 commented on GitHub (Jun 24, 2025): the bug still exist
Author
Owner

@xn1cklas commented on GitHub (Jul 1, 2025):

I had this issue when navigating to an authenticated page after sign-in. Before redirecting to the authenticated pages, after successful sign-in, I ran a refetch from the useSession hook. This fixed the problem.

It seems like local session state is not refetched after session creation.

<!-- gh-comment-id:3022697976 --> @xn1cklas commented on GitHub (Jul 1, 2025): I had this issue when navigating to an authenticated page after sign-in. Before redirecting to the authenticated pages, after successful sign-in, I ran a refetch from the useSession hook. This fixed the problem. It seems like local session state is not refetched after session creation.
Author
Owner

@bcheung commented on GitHub (Jul 2, 2025):

I used react query to manually refetch and periodically refetch the session to get around this useSession hook issue not triggering a rerender.
However, I am randomly getting a null session when calling get-session even though cookies are set. Something tells me it might be related to this useSession hook issue.
For more context and repro of the null session issue without using this useSession hook:
https://github.com/better-auth/better-auth/issues/2055#issuecomment-3017746698

<!-- gh-comment-id:3026008688 --> @bcheung commented on GitHub (Jul 2, 2025): I used react query to manually refetch and periodically refetch the session to get around this useSession hook issue not triggering a rerender. However, I am randomly getting a null session when calling get-session even though cookies are set. Something tells me it might be related to this useSession hook issue. For more context and repro of the null session issue without using this useSession hook: https://github.com/better-auth/better-auth/issues/2055#issuecomment-3017746698
Author
Owner

@harveylee commented on GitHub (Jul 10, 2025):

I had been having an issue with similar symptoms, which seemed to resolve when I disabled the cookieCache in the session options. Hope this helps someone else too.

<!-- gh-comment-id:3054908715 --> @harveylee commented on GitHub (Jul 10, 2025): I had been having an issue with similar symptoms, which seemed to resolve when I disabled the `cookieCache` in the `session` options. Hope this helps someone else too.
Author
Owner

@arjunsajeev commented on GitHub (Jul 15, 2025):

@Bekacru Please let us know if there are any updates. It seems the useSession hook is not getting the refreshed session when login is done using server actions. Several users seem to have reported this.

<!-- gh-comment-id:3074596001 --> @arjunsajeev commented on GitHub (Jul 15, 2025): @Bekacru Please let us know if there are any updates. It seems the `useSession` hook is not getting the refreshed session when login is done using server actions. Several users seem to have reported this.
Author
Owner

@einsteinsbrd commented on GitHub (Jul 16, 2025):

@Bekacru Please let us know if there are any updates. It seems the useSession hook is not getting the refreshed session when login is done using server actions. Several users seem to have reported this.

+1

<!-- gh-comment-id:3080648576 --> @einsteinsbrd commented on GitHub (Jul 16, 2025): > [@Bekacru](https://github.com/Bekacru) Please let us know if there are any updates. It seems the `useSession` hook is not getting the refreshed session when login is done using server actions. Several users seem to have reported this. +1
Author
Owner

@kawpii commented on GitHub (Jul 17, 2025):

I recently saw BetterAuth on HN, saying it received funding, hope they focus on resolving this issue soon, since this seems like a deal breaker for us developers.

<!-- gh-comment-id:3083847126 --> @kawpii commented on GitHub (Jul 17, 2025): I recently saw BetterAuth on HN, saying it received funding, hope they focus on resolving this issue soon, since this seems like a deal breaker for us developers.
Author
Owner

@SorooshGb commented on GitHub (Jul 21, 2025):

It’s been over a year and a half since this issue was opened — still no resolution?

For context: I’m using Better Auth with both the server and client setup.

When I sign in using authClient.signIn.email on the client, useSession() updates correctly.
But when I sign in using auth.api.signInEmail on the server (e.g. in a server action), the client session returned from useSession() doesn’t react to the updated auth state. I have to manually refresh the page for it to pick up the new session.

👉 The core issue seems to be: signing in with the server auth object doesn’t trigger a session update on the client authClient object.

<!-- gh-comment-id:3095831403 --> @SorooshGb commented on GitHub (Jul 21, 2025): **It’s been over a year and a half since this issue was opened — still no resolution?** For context: I’m using Better Auth with both the server and client setup. When I sign in using **authClient**.signIn.email on the client, useSession() updates correctly. But when I sign in using **auth**.api.signInEmail on the server (e.g. in a server action), the client session returned from useSession() doesn’t react to the updated auth state. I have to manually refresh the page for it to pick up the new session. 👉 The **core issue** seems to be: signing in with the server auth object doesn’t trigger a session update on the client authClient object.
Author
Owner

@vojtech-cerveny commented on GitHub (Jul 21, 2025):

I found a solution in the official Prisma documentation.
I think the tricky part is waiting for the session — and most of the time, it fails for all of us.

It fixed my problem where I had useSession() in layout.tsx, and the session wasn’t updating after logging in.
Only a page refresh worked.

With this piece of code, it works as expected — the session updates properly after sign-in and sign-out.

Try this approach:


"use client";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useSession } from "@/lib/auth-client";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { Button } from "../ui/button";
import { UserAvatar } from "../user/avatar";
import { SignOut } from "./sign-out";

export default function UserButton() {
  const router = useRouter();
  const { data: session, isPending } = useSession();

  useEffect(() => {
    if (!isPending && !session?.user) {
      router.push("/signin");
    }
  }, [isPending, session, router]);

  if (isPending) return <p className="mt-8 text-center">Loading...</p>;
  if (!session?.user) return <Link href="/signin">Sign in</Link>;

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <div className="rounded-full border-2 border-solid border-slate-500 hover:border-slate-200">
          <UserAvatar image={session.user.image ?? null} size={8} />
        </div>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="leading-none">{session.user.name}</p>
            <p className="text-xs leading-none text-muted-foreground">{session.user.email}</p>
          </div>
        </DropdownMenuLabel>
        <DropdownMenuItem>
          <Link href="/profile" className="w-full">
            <Button className="w-full">Profile</Button>
          </Link>
        </DropdownMenuItem>
        <DropdownMenuItem>
          <SignOut className="w-full" />
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
<!-- gh-comment-id:3099331631 --> @vojtech-cerveny commented on GitHub (Jul 21, 2025): I found a solution in the [official Prisma documentation](https://www.prisma.io/docs/guides/betterauth-nextjs#53-dashboard-page). I think the tricky part is **waiting for the session** — and most of the time, it fails for all of us. It fixed my problem where I had `useSession()` in `layout.tsx`, and the session **wasn’t updating after logging in**. Only a page refresh worked. With this piece of code, it works as expected — the session updates properly after sign-in and sign-out. **Try this approach:** ```typescript "use client"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { useSession } from "@/lib/auth-client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; import { Button } from "../ui/button"; import { UserAvatar } from "../user/avatar"; import { SignOut } from "./sign-out"; export default function UserButton() { const router = useRouter(); const { data: session, isPending } = useSession(); useEffect(() => { if (!isPending && !session?.user) { router.push("/signin"); } }, [isPending, session, router]); if (isPending) return <p className="mt-8 text-center">Loading...</p>; if (!session?.user) return <Link href="/signin">Sign in</Link>; return ( <DropdownMenu> <DropdownMenuTrigger asChild> <div className="rounded-full border-2 border-solid border-slate-500 hover:border-slate-200"> <UserAvatar image={session.user.image ?? null} size={8} /> </div> </DropdownMenuTrigger> <DropdownMenuContent className="w-56" align="end" forceMount> <DropdownMenuLabel className="font-normal"> <div className="flex flex-col space-y-1"> <p className="leading-none">{session.user.name}</p> <p className="text-xs leading-none text-muted-foreground">{session.user.email}</p> </div> </DropdownMenuLabel> <DropdownMenuItem> <Link href="/profile" className="w-full"> <Button className="w-full">Profile</Button> </Link> </DropdownMenuItem> <DropdownMenuItem> <SignOut className="w-full" /> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> ); } ```
Author
Owner

@ping-maxwell commented on GitHub (Aug 8, 2025):

To my knowledge, useSession will refetch session data if any session related endpoints were called on the same client. There's a listener part of useSession that will refetch that data once any one of those endpoints were hit.
If the session/user were to be updated on the server, the useSession's internal listener wouldn't know that and thus wouldn't trigger a session refetch.

It's a tricky problem to solve, as it would require the server to tell the client to refetch the session. This would require something like web-sockets which is definitely not a good solution.
Alternatively polling from the client side to check if a session update is due is possibly another solution, but isn't the greatest either.

<!-- gh-comment-id:3166246173 --> @ping-maxwell commented on GitHub (Aug 8, 2025): To my knowledge, useSession will refetch session data if any session related endpoints were called on the same client. There's a listener part of useSession that will refetch that data once any one of those endpoints were hit. If the session/user were to be updated on the server, the useSession's internal listener wouldn't know that and thus wouldn't trigger a session refetch. It's a tricky problem to solve, as it would require the server to tell the client to refetch the session. This would require something like web-sockets which is definitely not a good solution. Alternatively polling from the client side to check if a session update is due is possibly another solution, but isn't the greatest either.
Author
Owner

@widavies commented on GitHub (Aug 8, 2025):

I agree with @ping-maxwell here, you can't do anything about a server side session refresh unless you poll or push from the server to the client.

I have found however that not every client request actually updates the store. I had to do this to one of my requests to make it actually update the store:

await authClient
      .getSession({
        query: {
          disableCookieCache: true,
        },
        fetchOptions: {
          throw: true,
          onSuccess: (ctx) => {
            authClient.$store.atoms['session'].set({
              ...authClient.$store.atoms['session'].get(),
              data: ctx.data,
              headers: ctx.response.headers,
              isPending: false,
              isRefetching: false,
            });
          },
        },
      })
      .catch(() => {
        // Fallback to the old store value
        return authClient.$store.atoms['session'].get();
      });
<!-- gh-comment-id:3166255937 --> @widavies commented on GitHub (Aug 8, 2025): I agree with @ping-maxwell here, you can't do anything about a server side session refresh unless you poll or push from the server to the client. I have found however that not every client request actually updates the store. I had to do this to one of my requests to make it actually update the store: ```ts await authClient .getSession({ query: { disableCookieCache: true, }, fetchOptions: { throw: true, onSuccess: (ctx) => { authClient.$store.atoms['session'].set({ ...authClient.$store.atoms['session'].get(), data: ctx.data, headers: ctx.response.headers, isPending: false, isRefetching: false, }); }, }, }) .catch(() => { // Fallback to the old store value return authClient.$store.atoms['session'].get(); }); ```
Author
Owner

@widavies commented on GitHub (Aug 30, 2025):

@ping-maxwell One random thing to consider is that useSession (as I understand) only reacts to session changes in the current tab - not between tabs. This may be one reason that people perceive it as not reacting.

This also got me thinking about multi-device sessions. Changing the active session will update cookies across all tabs, but useSession is not aware of this and won't react. This also ends up splitting the session state - some is in the session nanostore, and some in the site cookies. This can be a problem in multi-device sessions for example, it might be nice to have one account signed in on one tab and another account on a different tab but as far as I understand it, this is not possible yet because of the cookies applying to all tabs.

<!-- gh-comment-id:3239555047 --> @widavies commented on GitHub (Aug 30, 2025): @ping-maxwell One random thing to consider is that `useSession` (as I understand) only reacts to session changes in the current tab - not between tabs. This may be one reason that people perceive it as not reacting. This also got me thinking about multi-device sessions. Changing the active session will update cookies across all tabs, but `useSession` is not aware of this and won't react. This also ends up splitting the session state - some is in the session nanostore, and some in the site cookies. This can be a problem in multi-device sessions for example, it might be nice to have one account signed in on one tab and another account on a different tab but as far as I understand it, this is not possible yet because of the cookies applying to all tabs.
Author
Owner

@ping-maxwell commented on GitHub (Aug 31, 2025):

@widavies Yeah.
One thing we have also considered is to refetch the session on tab focus, I don't believe this is already a feature but shouldn't be too hard to implement on each devs end if needed ASAP.
Essentially it's just a window.addEventListener("focus", () => refetch())

<!-- gh-comment-id:3239928888 --> @ping-maxwell commented on GitHub (Aug 31, 2025): @widavies Yeah. One thing we have also considered is to refetch the session on tab focus, I don't believe this is already a feature but shouldn't be too hard to implement on each devs end if needed ASAP. Essentially it's just a `window.addEventListener("focus", () => refetch())`
Author
Owner

@markaljhon commented on GitHub (Sep 23, 2025):

In my case, the issue was useSession or getSession weren't passing cookies to the header.

// auth.ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // ...other better-auth config
  advanced: {
    defaultCookieAttributes: {
      sameSite: "none", // this works for me
      secure: true
    }
  }
});
<!-- gh-comment-id:3322723879 --> @markaljhon commented on GitHub (Sep 23, 2025): In my case, the issue was `useSession` or `getSession` weren't passing cookies to the header. ```ts // auth.ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ // ...other better-auth config advanced: { defaultCookieAttributes: { sameSite: "none", // this works for me secure: true } } }); ```
Author
Owner

@ptts commented on GitHub (Oct 3, 2025):

(Context: TanStack Router Client-Side Only Setup, better-auth v1.3.25)

In my case, authClient.signIn.passkey({}) fails to properly update the session, but authClient.signIn.email() works fine.

As a workaround, I did:

const session = authClient.useSession();
// ...
const { data, error } = await authClient.signIn.passkey({
  fetchOptions: {
    onResponse: () => session.refetch(), // ⬅️
  },
});

<!-- gh-comment-id:3365498648 --> @ptts commented on GitHub (Oct 3, 2025): _(Context: TanStack Router Client-Side Only Setup, better-auth v1.3.25)_ In my case, `authClient.signIn.passkey({})` fails to properly update the session, but `authClient.signIn.email()` works fine. As a workaround, I did: ```ts const session = authClient.useSession(); // ... const { data, error } = await authClient.signIn.passkey({ fetchOptions: { onResponse: () => session.refetch(), // ⬅️ }, }); ```
Author
Owner

@ping-maxwell commented on GitHub (Oct 3, 2025):

@ptts I think your issue is related to https://github.com/better-auth/better-auth/issues/858

<!-- gh-comment-id:3366159762 --> @ping-maxwell commented on GitHub (Oct 3, 2025): @ptts I think your issue is related to https://github.com/better-auth/better-auth/issues/858
Author
Owner

@m-umar-ch commented on GitHub (Nov 22, 2025):

@kaushalyap is right. This worked for me too. Its problem of importing form client and not from react

We'all are importing like import { createAuthClient } from "better-auth/client"; . This will only work on the server side.

also make another instance of auth client like import { createAuthClient as reactCreateAuthClient } from "better-auth/react";. This will work in client side.

<!-- gh-comment-id:3566673231 --> @m-umar-ch commented on GitHub (Nov 22, 2025): @kaushalyap is right. This worked for me too. Its problem of importing form `client` and not from `react` We'all are importing like `import { createAuthClient } from "better-auth/client";` . This will only work on the server side. also make another instance of auth client like `import { createAuthClient as reactCreateAuthClient } from "better-auth/react";`. This will work in client side.
Author
Owner

@ping-maxwell commented on GitHub (Nov 22, 2025):

@m-umar-ch this is not the issue everyone is facing.

Unfortunately this github issue title is too general that it captures several different issues into the same thread.

<!-- gh-comment-id:3566865510 --> @ping-maxwell commented on GitHub (Nov 22, 2025): @m-umar-ch this is not the issue everyone is facing. Unfortunately this github issue title is too general that it captures several different issues into the same thread.
Author
Owner

@hartbit commented on GitHub (Jan 8, 2026):

I have found however that not every client request actually updates the store. I had to do this to one of my requests to make it actually update the store:

await authClient
      .getSession({
        query: {
          disableCookieCache: true,
        },
        fetchOptions: {
          throw: true,
          onSuccess: (ctx) => {
            authClient.$store.atoms['session'].set({
              ...authClient.$store.atoms['session'].get(),
              data: ctx.data,
              headers: ctx.response.headers,
              isPending: false,
              isRefetching: false,
            });
          },
        },
      })
      .catch(() => {
        // Fallback to the old store value
        return authClient.$store.atoms['session'].get();
      });

I've had the exact same issue (in Vue.js) where a client call to getSession after a user update on the server returns the correctly updated user in the response payload but other component's useSession reference don't get updated. I confirm that the workaround above resolved the issue. But I'd love for a better fix.

<!-- gh-comment-id:3725633987 --> @hartbit commented on GitHub (Jan 8, 2026): > I have found however that not every client request actually updates the store. I had to do this to one of my requests to make it actually update the store: > > ```js > await authClient > .getSession({ > query: { > disableCookieCache: true, > }, > fetchOptions: { > throw: true, > onSuccess: (ctx) => { > authClient.$store.atoms['session'].set({ > ...authClient.$store.atoms['session'].get(), > data: ctx.data, > headers: ctx.response.headers, > isPending: false, > isRefetching: false, > }); > }, > }, > }) > .catch(() => { > // Fallback to the old store value > return authClient.$store.atoms['session'].get(); > }); > ``` I've had the exact same issue (in Vue.js) where a client call to `getSession` after a user update on the server returns the correctly updated user in the response payload but other component's `useSession` reference don't get updated. I confirm that the workaround above resolved the issue. But I'd love for a better fix.
Author
Owner

@ping-maxwell commented on GitHub (Jan 9, 2026):

Hello all, this issue has been open since version 1.1.1 which is quite some time ago now, several fixes have been made since and lots have changed. This specific issue being very broad in the title/description has caught a lot of different cases which are unrelated to the OPs case. I'll be closing this issue as for most cases this should be solved, any new recent issues about this please open a new issue detailing your case - this will help us debug your situation easier.

Regarding a common issue I see appearing in this thread being related to the client's useSession not updating whenever user data changes when updating on the server. This is because there was no way for Better Auth to communicate to your front-end to alert changes and trigger state changes accordingly. This has now changed, and the auth-client now has polling functionality built-in to refetch session data periodically.

<!-- gh-comment-id:3727928799 --> @ping-maxwell commented on GitHub (Jan 9, 2026): Hello all, this issue has been open since version 1.1.1 which is quite some time ago now, several fixes have been made since and lots have changed. This specific issue being very broad in the title/description has caught a lot of different cases which are unrelated to the OPs case. I'll be closing this issue as for most cases this should be solved, any new recent issues about this please open a new issue detailing your case - this will help us debug your situation easier. Regarding a common issue I see appearing in this thread being related to the client's `useSession` not updating whenever user data changes when updating on the server. This is because there was no way for Better Auth to communicate to your front-end to alert changes and trigger state changes accordingly. This has now changed, and the auth-client now has polling functionality built-in to refetch session data periodically.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25867