phoskee
phoskee2w ago

Better Auth + Convex: isLoading never resolves and authenticated queries never run

"use client";
import {
Authenticated,
AuthLoading,
Unauthenticated,
useConvexAuth,
useMutation,
useQuery,
} from "convex/react";
import { useState } from "react";
import { api } from "../../convex/_generated/api";

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


export default function Home() {
const [inputValue, setInputValue] = useState("");
const { isAuthenticated } = useConvexAuth();
const tests = useQuery(api.test.getTest);
const insertText = useMutation(api.test.addTest);
const testAuth = useQuery(api.test.testAuth, isAuthenticated ? {} : "skip");
const user2 = useQuery(
api.auth.getCurrentUser,
isAuthenticated ? {} : "skip"
);
console.log("testAuth:", testAuth);

function handleInsert() {
insertText({ test: inputValue })
.then(() => {
setInputValue("");
})
.catch((error) => {
console.error("Error inserting text:", error);
});
}

async function handleSingUp() {
const { data, error } = await authClient.signUp.email({
name: "John Doe", // required
email: "john.doe@example.com", // required
password: "password1234", // required
image: "https://example.com/image.png",
});
console.log("Sign-up data:", data, "Error:", error);
}


"use client";
import {
Authenticated,
AuthLoading,
Unauthenticated,
useConvexAuth,
useMutation,
useQuery,
} from "convex/react";
import { useState } from "react";
import { api } from "../../convex/_generated/api";

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


export default function Home() {
const [inputValue, setInputValue] = useState("");
const { isAuthenticated } = useConvexAuth();
const tests = useQuery(api.test.getTest);
const insertText = useMutation(api.test.addTest);
const testAuth = useQuery(api.test.testAuth, isAuthenticated ? {} : "skip");
const user2 = useQuery(
api.auth.getCurrentUser,
isAuthenticated ? {} : "skip"
);
console.log("testAuth:", testAuth);

function handleInsert() {
insertText({ test: inputValue })
.then(() => {
setInputValue("");
})
.catch((error) => {
console.error("Error inserting text:", error);
});
}

async function handleSingUp() {
const { data, error } = await authClient.signUp.email({
name: "John Doe", // required
email: "john.doe@example.com", // required
password: "password1234", // required
image: "https://example.com/image.png",
});
console.log("Sign-up data:", data, "Error:", error);
}


5 Replies
Convex Bot
Convex Bot2w ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
phoskee
phoskeeOP2w ago
async function handleSignOut() {
await authClient.signOut();
}
return (
...
<span>
{user2?.name}
</span>
<Unauthenticated>Logged out</Unauthenticated>
<AuthLoading>Loading...</AuthLoading>
<Authenticated>
<span>
{testAuth!
? `testAuth works, ${testAuth.identity}`
: "testAuth failed"}
</span>
<span>
{user2! ? `Welcome, ${user2.name} ` : "User not found"}
</span>
<div className="grid gap-2 grid-cols-2">
<input
type="text"
className="ring"
value={inputValue}
onChange={(event) => setInputValue(event.target.value)}
/>
<button className="ring p-1" onClick={handleInsert}>
scrivi
</button>
</div>
<div>
{tests &&
tests.map((test) => (
<div key={test._id.toString()}>{test.text}</div>
))}
</div>
</Authenticated>
</div>
</h1>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<button onClick={handleSingUp}>sign up</button>
<button onClick={handleSignIn}>sign in</button>
<button onClick={handleSignOut}>sign out</button>
...
);
}
async function handleSignOut() {
await authClient.signOut();
}
return (
...
<span>
{user2?.name}
</span>
<Unauthenticated>Logged out</Unauthenticated>
<AuthLoading>Loading...</AuthLoading>
<Authenticated>
<span>
{testAuth!
? `testAuth works, ${testAuth.identity}`
: "testAuth failed"}
</span>
<span>
{user2! ? `Welcome, ${user2.name} ` : "User not found"}
</span>
<div className="grid gap-2 grid-cols-2">
<input
type="text"
className="ring"
value={inputValue}
onChange={(event) => setInputValue(event.target.value)}
/>
<button className="ring p-1" onClick={handleInsert}>
scrivi
</button>
</div>
<div>
{tests &&
tests.map((test) => (
<div key={test._id.toString()}>{test.text}</div>
))}
</div>
</Authenticated>
</div>
</h1>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<button onClick={handleSingUp}>sign up</button>
<button onClick={handleSignIn}>sign in</button>
<button onClick={handleSignOut}>sign out</button>
...
);
}
When integrating Better Auth with Convex in my Next.js app, after signing in with Better Auth, the Convex authentication state (useConvexAuth()) remains stuck in isLoading. As a result, isAuthenticated never becomes true, and any authenticated queries or mutations (e.g., useQuery(api.auth.getCurrentUser, isAuthenticated ? {} : "skip")) are never executed. The UI always shows the unauthenticated state, even though the sign-in with Better Auth succeeds and a token is issued. I have verified that: The provider configuration in auth.config.ts is correct and the domain matches. The Better Auth client is initialized with the convexClient() plugin. The Convex client is set up with ConvexBetterAuthProvider and the correct URL. The versions of convex, @convex-dev/better-auth, and better-auth match the recommended ones. There are no errors in the browser or server logs. Despite this, Convex never recognizes the authentication, and the app is stuck in a loading or logged-out state. This prevents any authenticated queries from running, as documented in the official guides Basic Usage: Authorization. Is there a known issue or additional configuration required to get Convex to recognize the Better Auth session and resolve the authentication state?
erquhart
erquhart2w ago
You probably have expectAuth: true on your Convex client - this pauses all queries from initial execution until the user is authenticated. Turn that off to troubleshoot what's happening with auth.
phoskee
phoskeeOP2w ago
Yes, I have it, but I also tested without it. Queries restart, but authentication still doesn't occur
Smultar
Smultar5d ago
I'm currently using the better auth session hook, to determined whos currently signed in. Does this conflict with telling, if the user is currently authenticated with convex. Is betterauth session != convex authenciation?

Did you find this page helpful?