#App Remounts on Navigation with Tanstack Router + Convex Auth

10 messages · Page 1 of 1 (latest)

outer harbor
#

I verified both Convex Auth and (Clerk + Convex) providers. For both solutions, it seems that the app unmounts for some reason between navigations until the auth state is determined. So even when I am authenticated and navigate between protected routes, the issue remains, and the isAuthenticated state is reset on page navigation.
_authed.tsx:

import { createFileRoute, Navigate, Outlet } from '@tanstack/react-router';
import { Authenticated, Unauthenticated } from 'convex/react';

export const Route = createFileRoute('/_authed')({
  component: AuthenticatedLayoutRoute,
});

function AuthenticatedLayoutRoute() {
  return (
    <>
      <Unauthenticated>
        <Navigate to="/signin" />;
      </Unauthenticated>
      <Authenticated>
        <Outlet />
      </Authenticated>
    </>
  );
}

app.tsx:

const convex = new ConvexReactClient(
  import.meta.env.VITE_CONVEX_URL as string,
  {
    verbose: true,
  }
);

// Initialize Tanstack Query client
const queryClient = new QueryClient();

// Create the router
const router = createRouter({
  routeTree,
  context: {
    queryClient,
    convexClient: convex,
  },
  defaultPreload: 'intent',
  defaultPreloadStaleTime: 0,
});

// Register the router instance for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

function InnerApp() {
  return <RouterProvider router={router} />;
}

export default function App() {
  return (
    <ClerkProvider publishableKey="pk_...">
      <ConvexProviderWithClerk client={convex} useAuth={useAuth}>
        <InnerApp />
      </ConvexProviderWithClerk>
    </ClerkProvider>
  );
}

CC: @grand juniper we disscused that issue a bit, I think it's good to have seperate topic about that

twilit gazelleBOT
#

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](https://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!
outer harbor
#

Like I mentioned above, same issue with Contex Auth Provider:

_authed.tsx

import { Loader } from '@/components/ui/loader';
import { createFileRoute, Navigate, Outlet } from '@tanstack/react-router';
import { Authenticated, AuthLoading, Unauthenticated } from 'convex/react';

export const Route = createFileRoute('/_authed')({
  component: AuthenticatedLayoutRoute,
});

function AuthenticatedLayoutRoute() {
  return (
    <>
      <AuthLoading>
        <Loader />
      </AuthLoading>
      <Unauthenticated>
        <Navigate to="/signin" />;
      </Unauthenticated>
      <Authenticated>
        <Outlet />
      </Authenticated>
    </>
  );
}

app.tsx

// Initialize Convex client
const convex = new ConvexReactClient(
  import.meta.env.VITE_CONVEX_URL as string,
  {
    verbose: true,
  }
);

// Initialize Convex Query client
const convexQueryClient = new ConvexQueryClient(convex);

// Initialize Tanstack Query client
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryKeyHashFn: convexQueryClient.hashFn(),
      queryFn: convexQueryClient.queryFn(),
    },
  },
});

// Connect Convex Query client to Tanstack Query client
convexQueryClient.connect(queryClient);

// Create the router
const router = routerWithQueryClient(
  createRouter({
    routeTree,
    context: {
      queryClient,
      convexClient: convex,
    },
    defaultPreload: 'intent',
    defaultPreloadStaleTime: 0,
  }),
  queryClient
);

// Register the router instance for type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

function InnerApp() {
  return <RouterProvider router={router} />;
}

export default function App() {
  return (
    <ConvexAuthProvider client={convex}>
      <InnerApp />
    </ConvexAuthProvider>
  );
}
lyric prawn
#

Following this discussion.. I’ve been encountering the same issue

outer harbor
#

I think I am doing something wrong, because it does the same thing without convex (tried with supabase)

#

I need to say it's Tanstack Router issue, or skill issue 😄

#

I created my own provider:

import { RouterProvider } from '@tanstack/react-router';
import { supabase } from './db';
import { useAuth } from './hooks/use-auth';
import { AuthProvider } from './providers/auth/auth-provider';
import { router } from './router';

function InnerApp() {
  const auth = useAuth();
  return <RouterProvider router={router} context={{ auth }} />;
}

export default function App() {
  return (
    <AuthProvider supabase={supabase}>
      <InnerApp />
    </AuthProvider>
  );
}

and use beforeLoad:

export const Route = createFileRoute('/_authed')({
  beforeLoad: ({ context }) => {
    const { session } = context.auth;
    if (!session) {
      throw redirect({ to: '/signin' });
    }
  },
  component: AuthenticatedLayoutRoute,
});

function AuthenticatedLayoutRoute() {
  return <Outlet />;
}

and it still share the same issue

outer harbor
#

I also checked with react router, and it shares the same issue

function Router() {
  return (
    <BrowserRouter>
      <Routes>
        <Route element={<AuthLayout />}>
          <Route path="signin" element={<SignInPage />} />
        </Route>

        <Route element={<AppLayout />}>
          <Route path="notes" element={<NotesPage />} />
          <Route path="tasks" element={<TasksPage />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

export default function App() {
  return (
    <ConvexAuthProvider client={convex}>
      <QueryClientProvider client={queryClient}>
        <Router />
      </QueryClientProvider>
    </ConvexAuthProvider>
  );
}
import { Authenticated } from 'convex/react';
import { Outlet } from 'react-router';
import { SidebarProvider, SidebarTrigger } from '../ui/sidebar';
import { AppSidebar } from './app-sidebar';

export function AppLayout() {
  return (
    <Authenticated>
      <SidebarProvider>
        <AppSidebar />
        <main className="flex-1">
          <SidebarTrigger />
          <Outlet />
        </main>
      </SidebarProvider>
    </Authenticated>
  );
}

Outlet with <AppSidebar /> gets unmounted every time I do page navigation.

outer harbor
#

Seems like the auth state is not persistent between page navigations, because each time I navigate to a different page, I see this:

#

CC: @grand juniper