#?

1 messages · Page 1 of 1 (latest)

prime scarab
#

why sometime when i stay idle, and switch pages on my nextjs app using convex and workos the pages white screens

edgy lavaBOT
#

Hi @prime scarab. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

This is a known issue area with Convex + Next.js + WorkOS; what you’re seeing is almost certainly related to how auth state and tokens refresh while your app has been idle.

From the threads:

  • When WorkOS refreshes its access token (default every ~5 minutes), the app can briefly go through a Loading / Unauthenticated state, which unmounts components and then remounts them once Convex has a valid token again. This can look like a brief white screen or “flash” when you come back after being idle and navigate. [WorkOS + Convex thread]
  • There are also reports of users getting logged out or seeing unauthenticated errors or UI flashes when returning after a laptop has been asleep or a tab has been idle, again tied to token refresh and re‑auth. [WorkOS + Convex thread]
  • Convex’s client will proactively ask for a fresh token ~10 seconds before expiry, but WorkOS’s hooks sometimes report a transient “loading” or “unauthenticated” state during that refresh, which causes the UI to blank out briefly. [WorkOS + Convex thread]

So the white screen after idling + page switch is almost certainly:

  1. Your WorkOS hooks briefly say “loading” / “unauthenticated” while refreshing the token.
  2. Your layout or route guards unmount the authenticated tree (or show a loading state).
  3. Once the new token is validated by Convex, everything remounts.

Mitigations discussed in the threads (not full fixes, but help):

#
  • In your useAuthFromAuthKit / useAuth wrapper passed to ConvexProviderWithAuth, avoid flipping to a “loading” or “unauthenticated” state during token refresh; e.g. cache a previous “authenticated” state and keep reporting isAuthenticated: true while WorkOS is just refreshing. [WorkOS + Convex hook examples]
  • Prefer using getAccessToken() from useAccessToken (guaranteed fresh) instead of relying on a possibly stale accessToken value, and have fetchAccessToken always call that, returning null only on real errors. [WorkOS + Convex getAccessToken fix]

However, the knowledge sources also say this behavior (flash / brief unauth) is still being actively worked on and not fully solved in all setups yet, especially with Next.js App Router. [WorkOS + Convex status]

If you want more concrete help, the next step would be:

  • Share your Convex client/provider + WorkOS integration code and layout structure (especially anything that conditionally renders based on loading / isAuthenticated), and
  • Try to reproduce with a very short WorkOS access token lifetime (e.g. 1 minute) so you can see the pattern more clearly, as suggested in the threads. [WorkOS token duration debugging]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@prime scarab Kapa.ai is still learning and improving, please let me know how I did by reacting below.

prime scarab
#

@edgy lava this is what im using:
'use client';

import { ReactNode, useCallback, useRef, useEffect } from 'react';
import { ConvexReactClient } from 'convex/react';
import { ConvexProviderWithAuth } from 'convex/react';
import { AuthKitProvider, useAuth, useAccessToken } from '@workos-inc/authkit-nextjs/components';

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export function ConvexClientProvider({ children }: { children: ReactNode }) {
return (
<AuthKitProvider>
<ConvexProviderWithAuth client={convex} useAuth={useAuthFromAuthKit}>
{children}
</ConvexProviderWithAuth>
</AuthKitProvider>
);
}

function useAuthFromAuthKit() {
const { user, loading: isLoading } = useAuth();
const { accessToken, loading: tokenLoading, error: tokenError } = useAccessToken();
const loading = (isLoading ?? false) || (tokenLoading ?? false);
const authenticated = !!user && !!accessToken && !loading;

const stableAccessToken = useRef<string | null>(null);

useEffect(() => {
    if (accessToken && !tokenError) {
        stableAccessToken.current = accessToken;
    }
}, [accessToken, tokenError]);

const fetchAccessToken = useCallback(async () => {
    if (stableAccessToken.current && !tokenError) {
        return stableAccessToken.current;
    }
    return null;
}, [tokenError]);

return {
    isLoading: loading,
    isAuthenticated: authenticated,
    fetchAccessToken,
};

}

edgy lavaBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Your white screen is very likely coming from how isLoading / isAuthenticated are computed in your useAuthFromAuthKit hook during token refreshes.

From the Discord thread, people saw the same behavior (brief unauth / loading flashes, components unmounting, white screen) and fixed it by:

  1. Not treating token refresh as “loading” for Convex.
  2. Using getAccessToken() instead of the possibly stale accessToken value. [WorkOS getAccessToken fix]

Your current logic:

const loading = (isLoading ?? false) || (tokenLoading ?? false);
const authenticated = !!user && !!accessToken && !loading;

So when WorkOS is refreshing:

  • tokenLoading can be true
  • loading becomes true
  • authenticated becomes false
  • Convex sees isAuthenticated: false and/or isLoading: true, and your <Authenticated> / <AuthLoading> UI likely unmounts → white screen.

Recommended change

Based on the working patterns in the thread, adjust your hook to:

  • Consider the user authenticated as soon as user exists, even while the token is refreshing.
  • Use getAccessToken() to always get a fresh token for Convex.
  • Only report isLoading during the initial load, not during refreshes.

A minimal version aligned with what others reported fixing their “idle → white screen” issue is:

function useAuthFromAuthKit() {
  const { user, loading: isUserLoading } = useAuth();
  const {
    getAccessToken,
    loading: isTokenLoading,
    error: tokenError,
  } = useAccessToken();

  const isLoading = (isUserLoading ?? false) || (isTokenLoading ?? false);
  const isAuthenticated = !!user;
#

const fetchAccessToken = useCallback(async () => {
if (tokenError) return null;
const token = await getAccessToken();
return token ?? null;
}, [getAccessToken, tokenError]);

return {
isLoading,
isAuthenticated,
fetchAccessToken,
};
}


This pattern (using `getAccessToken` and not tying `isAuthenticated` to `accessToken`/`tokenLoading`) is exactly what another user used to stop unauthenticated flashes after idle / refresh. [[WorkOS getAccessToken fix](<https://discord.com/channels/1019350475847499849/1202520608672317471>)]

If you still see a flash, the thread also suggests an extra refinement: track an “initially loaded” flag so that `isLoading` is only `true` on first load, not during background refreshes. [[WorkOS loading-state tweaks](<https://discord.com/channels/1019350475847499849/1202520608672317471>)]
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@prime scarab Kapa.ai is still learning and improving, please let me know how I did by reacting below.

prime scarab
#

@edgy lava i dont mind hte flash, is just that when it happens my page stays blank

edgy lavaBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources don’t show a case where the page stays blank permanently; they only describe brief flashes / rerenders during token refresh. So I can’t say with certainty what’s causing a persistent white screen in your specific app.

What we do know from the WorkOS + Convex threads:

  • The Convex client will keep trying to authenticate and reconnect when tokens expire; even when there’s a refresh race, queries eventually resume and the UI comes back. [WorkOS + Convex refresh behavior]
  • The “blank for a split second” issue was fixed for some people by changing the useAuthFromAuthKit wrapper (like the getAccessToken version I showed) and by tightening the loading condition (e.g. including !accessToken in the loading boolean). [WorkOS loading-state tweaks]

If your page stays blank, that usually means your UI logic is never leaving a “loading” or “unauthenticated” branch. The docs explicitly recommend:

  • Use Convex’s <Authenticated>, <Unauthenticated>, <AuthLoading> and useConvexAuth() to drive UI, not WorkOS’s useAuth() directly. [Convex + AuthKit docs]

So the most likely causes (based on the sources) are:

  1. Your useAuthFromAuthKit is returning isLoading: true or isAuthenticated: false indefinitely after a refresh edge case.
  2. Your layout / routes render nothing (or only a loading screen) when isLoading is true or isAuthenticated is false, and that state never flips back.

To debug concretely:

  • Log what useAuthFromAuthKit returns over time:

    console.log('auth state', { isLoading, isAuthenticated });
    
  • Also log useConvexAuth() in a simple component:

#
  import { useConvexAuth } from 'convex/react';

  function DebugAuth() {
    const { isAuthenticated, isLoading } = useConvexAuth();
    console.log('convex auth', { isAuthenticated, isLoading });
    return null;
  }

If useConvexAuth() does eventually show isAuthenticated: true but your page is still blank, the issue is in your own conditional rendering (e.g. a guard that never re-renders). If useConvexAuth() stays unauthenticated forever after idle, that’s closer to the race/refresh issues being discussed and would be worth turning into a minimal repro and filing on the WorkOS template repos, as suggested in the thread. [WorkOS template issues]

Given the limitations of the sources, I can’t point to a specific bug beyond this; the next step is to instrument those auth values and see which one is “stuck” when the page stays blank.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: