Skip to content

Conversation

@chenxin-yan
Copy link

@chenxin-yan chenxin-yan commented Oct 24, 2025

As discussed in this discord thread, I've created a minimal demo repo to showcase the DX and UX differences between with/without skip option for usePreloadedQuery

The primary motivation for this is it would be nice to have an elegant and intuitive api for waiting for auth token to be loaded on client without blocking other parts of the component. With current api, to achieve this you can create another client component and wrap it with <Authenticated/>, but it becomes tedious in a lot of cases where you do not want to decouple the rendering logic to another component.

before:

import { preloadQuery } from "convex/nextjs";
import { getAuthToken } from "@/lib/convex";
import { api } from "../../../convex/_generated/api";
import { AuthenticatedContent } from "./components/AuthenticatedContent";
import { TodoList } from "./components/TodoList";

const OldPage = async () => {
  const token = await getAuthToken();
  const preloadedTodos = await preloadQuery(api.tasks.get, {}, { token });

  return (
    <AuthenticatedContent>
      <TodoList preloadedTodos={preloadedTodos} />
    </AuthenticatedContent>
  );
};

export default OldPage;
"use client";

import { type Preloaded, usePreloadedQuery } from "convex/react";
import type { api } from "../../../../convex/_generated/api";

interface Props {
  preloadedTodos: Preloaded<typeof api.tasks.get>;
}

export function TodoList({ preloadedTodos }: Props) {
  const todos = usePreloadedQuery(preloadedTodos);

  return (
    <>
      <h1>some todos:</h1>
      <ul>
        {todos.map((todo) => (
          <li key={todo._id}>{todo.text}</li>
        ))}
      </ul>
      <p>above are my todos</p>
    </>
  );
}

2025-10-23 23 22 43

After:

import { getAuthToken } from "@/lib/convex";
import { preloadQuery } from "convex/nextjs";
import { api } from "../../../convex/_generated/api";
import { TodoList } from "./components/TodoList";

const NewPage = async () => {
  const token = await getAuthToken();
  const preloadedTodos = await preloadQuery(api.tasks.get, {}, { token });

  return <TodoList preloadedTodos={preloadedTodos} />;
};

export default NewPage;
"use client";

import { type Preloaded, useConvexAuth, usePreloadedQuery } from "convex/react";
import type { api } from "../../../../convex/_generated/api";

interface Props {
  preloadedTodos: Preloaded<typeof api.tasks.get>;
}

export function TodoList({ preloadedTodos }: Props) {
  const { isLoading } = useConvexAuth();
  const todos = usePreloadedQuery(preloadedTodos, { skip: isLoading });

  return (
    <>
      <h1>some todos:</h1>
      <ul>
        {todos ? (
          todos.map((todo) => <li key={todo._id}>{todo.text}</li>)
        ) : (
          <p>loading...</p>
        )}
      </ul>
      <p>above are my todos</p>
    </>
  );
}

2025-10-23 23 23 28

Closes #98

I am not sure if I missed anything from documentation that can handle this more elegantly, but it is weird to me that you can skip useQuery but not usePreloadedQuery.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

error when using preload query with authentication

1 participant