Chenxin_Yan
Chenxin_Yan3w ago

How to use preload query with authentication

I am referring to this documentation Here are the components:
import { getAuthToken } from "@/lib/convex";
import { api } from "@dev-team-fall-25/server/convex/_generated/api";
import { preloadQuery } from "convex/nextjs";
import { ScheduleContent } from "./components/schedule-content";

const SchedulePage = async () => {
const token = await getAuthToken();
const preloadedClasses = await preloadQuery(
api.userCourseOfferings.getUserCourseOfferings,
{},
{ token },
);

return <ScheduleContent preloadedClasses={preloadedClasses} />;
};

export default SchedulePage;
import { getAuthToken } from "@/lib/convex";
import { api } from "@dev-team-fall-25/server/convex/_generated/api";
import { preloadQuery } from "convex/nextjs";
import { ScheduleContent } from "./components/schedule-content";

const SchedulePage = async () => {
const token = await getAuthToken();
const preloadedClasses = await preloadQuery(
api.userCourseOfferings.getUserCourseOfferings,
{},
{ token },
);

return <ScheduleContent preloadedClasses={preloadedClasses} />;
};

export default SchedulePage;
"use client";

import { api } from "@dev-team-fall-25/server/convex/_generated/api";
import { Preloaded, usePreloadedQuery } from "convex/react";
import { ScheduleCalendar } from "./schedule-calendar";

interface ScheduleContentProps {
preloadedClasses: Preloaded<
typeof api.userCourseOfferings.getUserCourseOfferings
>;
}

export function ScheduleContent({ preloadedClasses }: ScheduleContentProps) {
const classes = usePreloadedQuery(preloadedClasses);

return <ScheduleCalendar classes={classes} />;
}
"use client";

import { api } from "@dev-team-fall-25/server/convex/_generated/api";
import { Preloaded, usePreloadedQuery } from "convex/react";
import { ScheduleCalendar } from "./schedule-calendar";

interface ScheduleContentProps {
preloadedClasses: Preloaded<
typeof api.userCourseOfferings.getUserCourseOfferings
>;
}

export function ScheduleContent({ preloadedClasses }: ScheduleContentProps) {
const classes = usePreloadedQuery(preloadedClasses);

return <ScheduleCalendar classes={classes} />;
}
When loading the schedule page, I got this Not Authenticted error. The reason is that when usePreloadedQuery is launched, the auth token is not loaded yet. I have to create another client side component that wraps the schedule content in <Authenticated/>, however, this lead to the entire page to wait til the auth token to be loaded. Is there a way to skip the usePreloadedQuery like the way you can skip useQuery or other alternative?
Next.js Server Rendering | Convex Developer Hub
Implement server-side rendering with Convex in Next.js App Router using preloadQuery, fetchQuery, and server actions for improved performance.
5 Replies
Convex Bot
Convex Bot3w 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!
erquhart
erquhart3w ago
This is from a Better Auth example, but it shows how to capture preloaded state and render it while waiting for auth, works the same for any auth provider: https://github.com/get-convex/better-auth/blob/c325752643dd6a21d1629976dd5ac95fd2fee293/examples/next/app/(auth)/dashboard/server/todo-items.tsx
Chenxin_Yan
Chenxin_YanOP3w ago
Yes, I am using the <Authenticated/> component that convex/react provides which does the same thing in the example essensially. In the example, it uses this:
const { isLoading } = useConvexAuth();
const preloadedTodos = usePreloadedQuery(preloadedTodosQuery);
const [todos, setTodos] = useState(preloadedTodos);

useEffect(() => {
if (!isLoading) {
setTodos(preloadedTodos);
}
}, [preloadedTodos, isLoading]);
const { isLoading } = useConvexAuth();
const preloadedTodos = usePreloadedQuery(preloadedTodosQuery);
const [todos, setTodos] = useState(preloadedTodos);

useEffect(() => {
if (!isLoading) {
setTodos(preloadedTodos);
}
}, [preloadedTodos, isLoading]);
which imo is not elegant since you need to keep an internal state using useState. Ideally, usePreloadQuery should have a second arg just like useQuery that takes in skip to dynamically turn on or off the query. Currently, if I want to do it, I need to create a wrapper client compoennt that wraps everything in which I wait for the token to be loaded. After it loads, render the component with usePrelaodedQuery call, which is just an overkill and we have to do it for all the components that uses this SSR pattern. or even better, when using preload in the server component, the preloadedTodos should contain the token needed for the query on the client to run and pass along everything, which results in a better DX. The mental model for developer would intuitively be: 1. I pass the token to the preload function, gettng the preloadedData with the token: 2. pass the preloadedData with token into usePreloadQuery 3. it works @erquhart Let me know if I missed anything. I havn't got a chance to look at the source code, but if you think this is a good idea, I'm happy to take a look and open a PR.
erquhart
erquhart3w ago
We know about this shortcoming, I don't think a PR is the answer on this since it's pretty simple to implement, more of a question of api design, we just haven't gotten there yet. But agree it could be nicer.
Chenxin_Yan
Chenxin_YanOP3w ago
tbh, the one thing that I care about for now about this is that when I am doing SSR with convex, I should be able to render the shell immediately (in an intuitive and elegant way) and its fine for the data fetching to wait for the auth token. Currently, if I just use the preload workflow as desscribed, the component that contains the usePreloadQuery has to be blocked to wait for the auth token entirely or you would get the Not Authorized error. The simple fix that I proposed that would work with this is just add a second argument to usePreloadQuery() to take in skip just like how tanstack query has an enabled arg to control the query activation dynamically. This is already impelmented with convex useQuery which can take optional rest args or skip literal string to enable/disable the query here.

Did you find this page helpful?