[GH-ISSUE #7230] [TanStack Start + Convex + Better Auth] SSR auth check runs on every client navigation, causing slow UX #28087

Open
opened 2026-04-17 19:29:12 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @ivanmeda993 on GitHub (Jan 9, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7230

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Problem

When using Better Auth with TanStack Start and Convex, the recommended SSR pattern causes getAuth() to run on every client-side navigation, not just during SSR.

beforeLoad: async (ctx) => {
  const token = await getAuth() // Runs on EVERY route change
}

This makes navigation feel slow due to unnecessary server round-trips.

Expected Behavior

  • Auth validation during SSR only
  • Client-side navigation should use cached token state
  • No server call on every click

Related

https://github.com/get-convex/better-auth/issues/210

Environment

  • TanStack Start
  • Convex
  • Better Auth

Current vs. Expected behavior

Current behavior:
getAuth() runs on every client-side navigation (every click), causing unnecessary server round-trips and slow UX.

Expected behavior:
Auth validation should run during SSR only. Client-side navigation should use cached token state without server calls.

What version of Better Auth are you using?

1.4.9

System info

{
  
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.5.2"
  },
  "frameworks": [
    {
      "name": "react",
      "version": "^19.0.0"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "1.4.9",
    "config": null
  }
}

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

Client

Auth config (if applicable)

import { betterAuth } from "better-auth/minimal";
import { createClient } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import authConfig from "./auth.config";
import { components } from "./_generated/api";
import { query } from "./_generated/server";
import type { GenericCtx } from "@convex-dev/better-auth";
import type { DataModel } from "./_generated/dataModel";

const siteUrl = process.env.SITE_URL!;

// The component client has methods needed for integrating Convex with Better Auth,
// as well as helper methods for general use.
export const authComponent = createClient<DataModel>(components.betterAuth);

export const createAuth = (ctx: GenericCtx<DataModel>) => {
  return betterAuth({
    baseURL: siteUrl,
    database: authComponent.adapter(ctx),
    // Configure simple, non-verified email/password to get started
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false,
    },
    plugins: [
      // The Convex plugin is required for Convex compatibility
      convex({ authConfig }),
    ],
  });
};

Additional context

No response

Originally created by @ivanmeda993 on GitHub (Jan 9, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7230 Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ## Problem When using Better Auth with TanStack Start and Convex, the recommended SSR pattern causes `getAuth()` to run on **every client-side navigation**, not just during SSR. ```typescript beforeLoad: async (ctx) => { const token = await getAuth() // Runs on EVERY route change } ``` This makes navigation feel slow due to unnecessary server round-trips. ## Expected Behavior - Auth validation during SSR only - Client-side navigation should use cached token state - No server call on every click ## Related https://github.com/get-convex/better-auth/issues/210 ## Environment - TanStack Start - Convex - Better Auth ### Current vs. Expected behavior **Current behavior:** `getAuth()` runs on every client-side navigation (every click), causing unnecessary server round-trips and slow UX. **Expected behavior:** Auth validation should run during SSR only. Client-side navigation should use cached token state without server calls. ### What version of Better Auth are you using? 1.4.9 ### System info ```bash { "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.5.2" }, "frameworks": [ { "name": "react", "version": "^19.0.0" } ], "databases": null, "betterAuth": { "version": "1.4.9", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth/minimal"; import { createClient } from "@convex-dev/better-auth"; import { convex } from "@convex-dev/better-auth/plugins"; import authConfig from "./auth.config"; import { components } from "./_generated/api"; import { query } from "./_generated/server"; import type { GenericCtx } from "@convex-dev/better-auth"; import type { DataModel } from "./_generated/dataModel"; const siteUrl = process.env.SITE_URL!; // The component client has methods needed for integrating Convex with Better Auth, // as well as helper methods for general use. export const authComponent = createClient<DataModel>(components.betterAuth); export const createAuth = (ctx: GenericCtx<DataModel>) => { return betterAuth({ baseURL: siteUrl, database: authComponent.adapter(ctx), // Configure simple, non-verified email/password to get started emailAndPassword: { enabled: true, requireEmailVerification: false, }, plugins: [ // The Convex plugin is required for Convex compatibility convex({ authConfig }), ], }); }; ``` ### Additional context _No response_
GiteaMirror added the platformneeds: infocore labels 2026-04-17 19:29:13 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 9, 2026):

It seems you're experiencing an issue where getAuth() runs on every client-side navigation when using Better Auth with TanStack Start and Convex. This behavior can slow down your application due to unnecessary server round-trips.

To resolve this, it’s recommended to adjust your implementation so that getAuth() is only called during server-side rendering (SSR), and leverage a cached token state for client-side navigation. Unfortunately, there is no mentioned solution for this issue directly in the Better Auth documents; it might be worth checking the related discussions in the GitHub issue you referenced [1] for potential workarounds or updates.

In case you need more specific guidance, consider reaching out to the Better Auth team members, Beka and Max, by tagging them in your query.

Have you had a chance to look into caching methods or managing session states locally on the client-side? I'd love to help with any further questions you might have!

If you need more help, tag @better-auth-agent in a comment so I can respond.

Diagram • Discord • GitHub

Diagram Join Star

<!-- gh-comment-id:3730731332 --> @better-auth-agent[bot] commented on GitHub (Jan 9, 2026): It seems you're experiencing an issue where `getAuth()` runs on every client-side navigation when using Better Auth with TanStack Start and Convex. This behavior can slow down your application due to unnecessary server round-trips. To resolve this, it’s recommended to adjust your implementation so that `getAuth()` is only called during server-side rendering (SSR), and leverage a cached token state for client-side navigation. Unfortunately, there is no mentioned solution for this issue directly in the Better Auth documents; it might be worth checking the related discussions in the GitHub issue you referenced [[1]](https://github.com/better-auth/better-auth/pull/4060) for potential workarounds or updates. In case you need more specific guidance, consider reaching out to the Better Auth team members, Beka and Max, by tagging them in your query. Have you had a chance to look into caching methods or managing session states locally on the client-side? I'd love to help with any further questions you might have! _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@dosubot[bot] commented on GitHub (Jan 9, 2026):

Hi @ivanmeda993! I'm Dosu and I’m helping the better-auth team.

This happens because TanStack Start's beforeLoad hook runs on every route change—including client-side navigations—so getAuth() is called repeatedly, causing slow UX due to unnecessary server round-trips. Better Auth's client does cache session state, but the hook itself doesn't distinguish SSR from client navigation, so you get redundant calls unless you optimize the guard logic.

To avoid this, you can check the beforeLoad parameters (preload, cause) and skip the auth check unless it's an SSR load or a true route entry. For example:

beforeLoad: async ({ preload, cause }) => {
  // Only run auth check on SSR or initial navigation
  if (preload || cause !== "enter") return;
  const token = await getAuth();
  // ...rest of your logic
}

Reference

Another pattern is to fetch the session server-side in your root route's beforeLoad, then pass it down via context to child routes. This way, client-side navigation uses the cached session from context, avoiding extra network requests:

// In your root route
beforeLoad: async () => {
  const session = await getServerSession();
  return { session };
}

// In child routes
beforeLoad: ({ context: { session } }) => {
  if (!session?.user) throw redirect({ to: "/sign-in" });
}

Reference

Both approaches help reduce unnecessary server calls and improve navigation speed. If you use preloading or advanced caching (like react-query), be aware of possible duplicated state management and complexity. The community recognizes this as a common issue, and these patterns are the current best practice for TanStack Start + Better Auth setups.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3730749632 --> @dosubot[bot] commented on GitHub (Jan 9, 2026): <!-- Greeting --> Hi @ivanmeda993! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This happens because TanStack Start's `beforeLoad` hook runs on every route change—including client-side navigations—so `getAuth()` is called repeatedly, causing slow UX due to unnecessary server round-trips. Better Auth's client does cache session state, but the hook itself doesn't distinguish SSR from client navigation, so you get redundant calls unless you optimize the guard logic. To avoid this, you can check the `beforeLoad` parameters (`preload`, `cause`) and skip the auth check unless it's an SSR load or a true route entry. For example: ```typescript beforeLoad: async ({ preload, cause }) => { // Only run auth check on SSR or initial navigation if (preload || cause !== "enter") return; const token = await getAuth(); // ...rest of your logic } ``` [Reference](https://github.com/better-auth/better-auth/issues/1009) Another pattern is to fetch the session server-side in your root route's `beforeLoad`, then pass it down via context to child routes. This way, client-side navigation uses the cached session from context, avoiding extra network requests: ```typescript // In your root route beforeLoad: async () => { const session = await getServerSession(); return { session }; } // In child routes beforeLoad: ({ context: { session } }) => { if (!session?.user) throw redirect({ to: "/sign-in" }); } ``` [Reference](https://github.com/better-auth/better-auth/issues/1009) Both approaches help reduce unnecessary server calls and improve navigation speed. If you use preloading or advanced caching (like react-query), be aware of possible duplicated state management and complexity. The community recognizes this as a common issue, and these patterns are the current best practice for TanStack Start + Better Auth setups. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/97e47e40-4391-4ed7-bcb2-678d82041cb9?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20happens%20because%20TanStack%20Start%27s%20%60beforeLoad%60%20hook%20runs%20on%20every%20route%20change%E2%80%94including%20client-side%20navigations%E2%80%94so%20%60getAuth%28%29%60%20is%20called%20repeatedly%2C%20causing%20slow%20UX%20due%20to%20unnecessary%20server%20round-trips.%20Better%20Auth%27s%20client%20does%20cache%20session%20state%2C%20but%20the%20hook%20itself%20doesn%27t%20distinguish%20SSR%20from%20client%20navigation%2C%20so%20you%20get%20redundant%20calls%20unless%20you%20optimize%20the%20guard%20logic.%0A%0ATo%20avoid%20this%2C%20you%20can%20check%20the%20%60beforeLoad%60%20parameters%20%28%60preload%60%2C%20%60cause%60%29%20and%20skip%20the%20auth%20check%20unless%20it%27s%20an%20SSR%20load%20or%20a%20true%20route%20entry.%20For%20example%3A%0A%0A%60%60%60typescript%0AbeforeLoad%3A%20async%20%28%7B%20preload%2C%20cause%20%7D%29%20%3D%3E%20%7B%0A%20%20//%20Only%20run%20auth%20check%20on%20SSR%20or%20initial%20navigation%0A%20%20if%20%28preload%20%7C%7C%20cause%20%21%3D%3D%20%22enter%22%29%20return%3B%0A%20%20const%20token%20%3D%20await%20getAuth%28%29%3B%0A%20%20//%20...rest%20of%20your%20logic%0A%7D%0A%60%60%60%0A%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/1009%29%0A%0AAnother%20pattern%20is%20to%20fetch%20the%20session%20server-side%20in%20your%20root%20route%27s%20%60beforeLoad%60%2C%20then%20pass%20it%20down%20via%20context%20to%20child%20routes.%20This%20way%2C%20client-side%20navigation%20uses%20the%20cached%20session%20from%20context%2C%20avoiding%20extra%20network%20requests%3A%0A%0A%60%60%60typescript%0A//%20In%20your%20root%20route%0AbeforeLoad%3A%20async%20%28%29%20%3D%3E%20%7B%0A%20%20const%20session%20%3D%20await%20getServerSession%28%29%3B%0A%20%20return%20%7B%20session%20%7D%3B%0A%7D%0A%0A//%20In%20child%20routes%0AbeforeLoad%3A%20%28%7B%20context%3A%20%7B%20session%20%7D%20%7D%29%20%3D%3E%20%7B%0A%20%20if%20%28%21session%3F.user%29%20throw%20redirect%28%7B%20to%3A%20%22/sign-in%22%20%7D%29%3B%0A%7D%0A%60%60%60%0A%5BReference%5D%28https%3A//github.com/better-auth/better-auth/issues/1009%29%0A%0ABoth%20approaches%20help%20reduce%20unnecessary%20server%20calls%20and%20improve%20navigation%20speed.%20If%20you%20use%20preloading%20or%20advanced%20caching%20%28like%20react-query%29%2C%20be%20aware%20of%20possible%20duplicated%20state%20management%20and%20complexity.%20The%20community%20recognizes%20this%20as%20a%20common%20issue%2C%20and%20these%20patterns%20are%20the%20current%20best%20practice%20for%20TanStack%20Start%20%2B%20Better%20Auth%20setups.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7230)
Author
Owner

@ivanmeda993 commented on GitHub (Jan 11, 2026):

Solution from Dosubot is not good.

<!-- gh-comment-id:3735512382 --> @ivanmeda993 commented on GitHub (Jan 11, 2026): Solution from Dosubot is not good.
Author
Owner

@dosubot[bot] commented on GitHub (Jan 11, 2026):

Thanks for the feedback—can you clarify what specifically didn't work for you with the previous solution? There are a few advanced patterns that might help, depending on your needs:

  • The most robust fix is to fetch the session server-side in your root route and inject it into context, so all client navigation uses that cached session instead of triggering new requests. This avoids repeated getAuth() calls on every navigation and is confirmed to work in TanStack Start setups with Better Auth and Convex. If you need a concrete example or run into issues with context propagation, let me know—there are some nuances with plugin order and cookie propagation that can break this pattern if not handled carefully (source, source).

  • If you’re seeing duplicate requests or hydration issues, wrapping your session hook in a hydration guard (like useIsMounted) or using a custom Provider pattern at the root can prevent redundant fetches and keep SSR and client state in sync (source).

  • If you use multiple session-related plugins, make sure the cookie-managing plugin is last in your plugins array, and ensure your middleware/server actions forward set-cookie headers from Better Auth responses to the client (source).

If you have a specific constraint (like needing to avoid any client-side fetches, or a particular caching strategy), or if you can share a minimal code example, I can help tailor the solution further. What’s the main blocker or pain point with the previous approach?

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3735529117 --> @dosubot[bot] commented on GitHub (Jan 11, 2026): <!-- Answer --> Thanks for the feedback—can you clarify what specifically didn't work for you with the previous solution? There are a few advanced patterns that might help, depending on your needs: - The most robust fix is to fetch the session server-side in your root route and inject it into context, so all client navigation uses that cached session instead of triggering new requests. This avoids repeated getAuth() calls on every navigation and is confirmed to work in TanStack Start setups with Better Auth and Convex. If you need a concrete example or run into issues with context propagation, let me know—there are some nuances with plugin order and cookie propagation that can break this pattern if not handled carefully ([source](https://github.com/better-auth/better-auth/issues/1009), [source](https://github.com/better-auth/better-auth/issues/5639)). - If you’re seeing duplicate requests or hydration issues, wrapping your session hook in a hydration guard (like useIsMounted) or using a custom Provider pattern at the root can prevent redundant fetches and keep SSR and client state in sync ([source](https://github.com/better-auth/better-auth/issues/4609)). - If you use multiple session-related plugins, make sure the cookie-managing plugin is last in your plugins array, and ensure your middleware/server actions forward set-cookie headers from Better Auth responses to the client ([source](https://github.com/better-auth/better-auth/issues/5639)). If you have a specific constraint (like needing to avoid any client-side fetches, or a particular caching strategy), or if you can share a minimal code example, I can help tailor the solution further. What’s the main blocker or pain point with the previous approach? <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/2a0e1584-d8be-45ff-a356-d864e68a6f4b?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Thanks%20for%20the%20feedback%E2%80%94can%20you%20clarify%20what%20specifically%20didn%27t%20work%20for%20you%20with%20the%20previous%20solution%3F%20There%20are%20a%20few%20advanced%20patterns%20that%20might%20help%2C%20depending%20on%20your%20needs%3A%0A%0A-%20The%20most%20robust%20fix%20is%20to%20fetch%20the%20session%20server-side%20in%20your%20root%20route%20and%20inject%20it%20into%20context%2C%20so%20all%20client%20navigation%20uses%20that%20cached%20session%20instead%20of%20triggering%20new%20requests.%20This%20avoids%20repeated%20getAuth%28%29%20calls%20on%20every%20navigation%20and%20is%20confirmed%20to%20work%20in%20TanStack%20Start%20setups%20with%20Better%20Auth%20and%20Convex.%20If%20you%20need%20a%20concrete%20example%20or%20run%20into%20issues%20with%20context%20propagation%2C%20let%20me%20know%E2%80%94there%20are%20some%20nuances%20with%20plugin%20order%20and%20cookie%20propagation%20that%20can%20break%20this%20pattern%20if%20not%20handled%20carefully%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/1009%29%2C%20%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/5639%29%29.%0A%0A-%20If%20you%E2%80%99re%20seeing%20duplicate%20requests%20or%20hydration%20issues%2C%20wrapping%20your%20session%20hook%20in%20a%20hydration%20guard%20%28like%20useIsMounted%29%20or%20using%20a%20custom%20Provider%20pattern%20at%20the%20root%20can%20prevent%20redundant%20fetches%20and%20keep%20SSR%20and%20client%20state%20in%20sync%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/4609%29%29.%0A%0A-%20If%20you%20use%20multiple%20session-related%20plugins%2C%20make%20sure%20the%20cookie-managing%20plugin%20is%20last%20in%20your%20plugins%20array%2C%20and%20ensure%20your%20middleware/server%20actions%20forward%20set-cookie%20headers%20from%20Better%20Auth%20responses%20to%20the%20client%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/5639%29%29.%0A%0AIf%20you%20have%20a%20specific%20constraint%20%28like%20needing%20to%20avoid%20any%20client-side%20fetches%2C%20or%20a%20particular%20caching%20strategy%29%2C%20or%20if%20you%20can%20share%20a%20minimal%20code%20example%2C%20I%20can%20help%20tailor%20the%20solution%20further.%20What%E2%80%99s%20the%20main%20blocker%20or%20pain%20point%20with%20the%20previous%20approach%3F)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7230)
Author
Owner

@bytaesu commented on GitHub (Jan 13, 2026):

Hi @ivanmeda993, I'm checking this 🧐

<!-- gh-comment-id:3742357091 --> @bytaesu commented on GitHub (Jan 13, 2026): Hi @ivanmeda993, I'm checking this 🧐
Author
Owner

@ivanmeda993 commented on GitHub (Jan 13, 2026):

Thanks, @bytaesu . If you need any additional info from me, please let me know.

<!-- gh-comment-id:3742528121 --> @ivanmeda993 commented on GitHub (Jan 13, 2026): Thanks, @bytaesu . If you need any additional info from me, please let me know.
Author
Owner

@bytaesu commented on GitHub (Jan 13, 2026):

Hi @ivanmeda993,

I set up app and checked things..


During SSR only (the only time serverHttpClient exists), set the auth token to make HTTP queries with.

  1. You can try getAuth() only on the server side
// __root.tsx

beforeLoad: async (ctx) => {                                                                                          
    // server                                                     
    if (ctx.context.convexQueryClient.serverHttpClient) {                                                               
      const token = await getAuth();                                                                                    
      if (token) {                                                                                                      
        ctx.context.convexQueryClient.serverHttpClient.setAuth(token);                                                  
      }                                                                                                                 
      return { isAuthenticated: !!token, token };                                                                       
    }                                                                                                                   
                                                                                                                        
    // client                                                        
    return {};                                                                                                          
  }

  1. Another option is to leverage TanStack Query utilities like ensureQueryData for caching, and use invalidateQueries to refresh the session state when needed...

I looked through the guides and example apps, and they felt quite complex. For example, there’s abstraction not only over @tanstack/react-query but also @convex-dev/react-query. That complexity seems to come from how Convex integrates TanStack libraries, so it’s not something Better Auth itself can change.

That said, I agree the example feels overly complex. Examples should be as simple as possible, so there’s definitely room for improvement. Feel free to let me know how I can help 🙏

<!-- gh-comment-id:3742652831 --> @bytaesu commented on GitHub (Jan 13, 2026): Hi @ivanmeda993, I set up app and checked things.. --- > During SSR only (the only time serverHttpClient exists), set the auth token to make HTTP queries with. 1. You can try `getAuth()` only on the server side ```ts // __root.tsx beforeLoad: async (ctx) => { // server if (ctx.context.convexQueryClient.serverHttpClient) { const token = await getAuth(); if (token) { ctx.context.convexQueryClient.serverHttpClient.setAuth(token); } return { isAuthenticated: !!token, token }; } // client return {}; } ``` --- 2. Another option is to leverage TanStack Query utilities like `ensureQueryData` for caching, and use `invalidateQueries` to refresh the session state when needed... --- I looked through the guides and example apps, and they felt quite complex. For example, there’s abstraction not only over `@tanstack/react-query` but also `@convex-dev/react-query`. That complexity seems to come from how Convex integrates TanStack libraries, so it’s not something Better Auth itself can change. That said, I agree the example feels overly complex. Examples should be as simple as possible, so there’s definitely room for improvement. Feel free to let me know how I can help 🙏
Author
Owner

@scyther commented on GitHub (Apr 6, 2026):

You can create a custom UseAuthSession hook with react-query to avoid duplicate requests. Also a stale time can be set to de-duplicate getSession queries for one minute.

import { authClient } from "@/lib/auth-client";

export const sessionQueryOptions = queryOptions({
 queryKey: ["session"],
 queryFn: async () => {
   const { data, error } = await authClient.getSession();
   if (error) {
     throw error;
   }
   return data;
 },
 staleTime: 60 * 1000, // 1 minute
});

export function useAuthSession() {
 return useQuery(sessionQueryOptions);
}

You may need to invalidate session query on logout, login , or any other session change. Like this:

                fetchOptions: {
                  onSuccess: () => {
                    queryClient.invalidateQueries({ queryKey: ["session"] });
                  },
                },
              });```
<!-- gh-comment-id:4190441768 --> @scyther commented on GitHub (Apr 6, 2026): You can create a custom UseAuthSession hook with react-query to avoid duplicate requests. Also a stale time can be set to de-duplicate getSession queries for one minute. ```import { queryOptions, useQuery } from "@tanstack/react-query"; import { authClient } from "@/lib/auth-client"; export const sessionQueryOptions = queryOptions({ queryKey: ["session"], queryFn: async () => { const { data, error } = await authClient.getSession(); if (error) { throw error; } return data; }, staleTime: 60 * 1000, // 1 minute }); export function useAuthSession() { return useQuery(sessionQueryOptions); } ``` You may need to invalidate session query on logout, login , or any other session change. Like this: ``` authClient.signOut({ fetchOptions: { onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["session"] }); }, }, });```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28087