Karolus
Karolus
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
any ideas?
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
CC: @Tom
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
No description
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
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>
);
}
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>
);
}
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.
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
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>
);
}
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 />;
}
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
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
I need to say it's Tanstack Router issue, or skill issue 😄
11 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
I think I am doing something wrong, because it does the same thing without convex (tried with supabase)
11 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
88 replies
CCConvex Community
Created by Karolus on 2/1/2025 in #support-community
App Remounts on Navigation with Tanstack Router + Convex Auth
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>
</>
);
}
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>
);
}
// 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>
);
}
11 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
but Provider is different so maybe re-mounting won't happen
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
hm I will try, but are you sure? Clerk integration also relies on the same logic.: https://docs.convex.dev/auth/clerk#logged-in-and-logged-out-views
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
Yeah, got it. Just pointing that the same problem will existing no matter if it's Convex Auth or Clerk + Convex. It's a pity, I really enjoyed setting up the Auth with Convex. Super simple, good docs, but because of that issue, I don't think it's usable at this moment, at least not in Tanstack Router.
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
No description
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
Tomorrow, I will switch to Clerk for auth and verify if beforeLoad fixes the issue.
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
I see, unfortunately, it causes anything that was already mounted to disappear for 1-2 seconds, like AppSidebar in my example
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
Yeah, seems like useConvexAuth() is at the component level, so it runs again for every navigation. I could probably store the auth state locally in Zustand and rely on it, but it's a bit of a stretch.
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
@Tom Actually, it happens on every navigation as well. I tested both <Authenticated> and useContextAuth; they both result in this 1s delay to determine the auth state.
export const Route = createFileRoute('/_authenticated')({
component: AuthetnicatedLayoutRoute,
});

function AuthetnicatedLayoutRoute() {
const { isAuthenticated, isLoading } = useConvexAuth();
const navigate = useNavigate();

useEffect(() => {
// Redirect to login page if user is not authenticated.
if (!isLoading && !isAuthenticated) {
navigate({ to: '/signin' });
}
}, [isLoading, isAuthenticated]);

if (isLoading && !isAuthenticated) {
return <Loader />; // Every page navigation, I see this loader for 1-2 seconds before isAuthenticated is determined
}

return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
<Outlet />
</main>
</SidebarProvider>
);
}
export const Route = createFileRoute('/_authenticated')({
component: AuthetnicatedLayoutRoute,
});

function AuthetnicatedLayoutRoute() {
const { isAuthenticated, isLoading } = useConvexAuth();
const navigate = useNavigate();

useEffect(() => {
// Redirect to login page if user is not authenticated.
if (!isLoading && !isAuthenticated) {
navigate({ to: '/signin' });
}
}, [isLoading, isAuthenticated]);

if (isLoading && !isAuthenticated) {
return <Loader />; // Every page navigation, I see this loader for 1-2 seconds before isAuthenticated is determined
}

return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
<Outlet />
</main>
</SidebarProvider>
);
}
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
Great @Tom ,thanks for the help again. I am looking forward to the improvements 🙂
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
The only thing I am noticing in this solution:
import { createFileRoute, Navigate, Outlet } from "@tanstack/react-router";
import { Authenticated, Unauthenticated, AuthLoading } from "convex/react";

export const Route = createFileRoute("/_auth")({
component: AuthLayout,
});

function AuthLayout() {
return (
<>
<AuthLoading>
<div>Loading authentication...</div>
</AuthLoading>
<Unauthenticated>
<Navigate to="/login" replace />
</Unauthenticated>
<Authenticated>
<Outlet />
</Authenticated>
</>
);
}
import { createFileRoute, Navigate, Outlet } from "@tanstack/react-router";
import { Authenticated, Unauthenticated, AuthLoading } from "convex/react";

export const Route = createFileRoute("/_auth")({
component: AuthLayout,
});

function AuthLayout() {
return (
<>
<AuthLoading>
<div>Loading authentication...</div>
</AuthLoading>
<Unauthenticated>
<Navigate to="/login" replace />
</Unauthenticated>
<Authenticated>
<Outlet />
</Authenticated>
</>
);
}
Loading authentication... is visible after every page refresh. So there is always this 1s delay before authentication state is determined.
88 replies
CCConvex Community
Created by Llabe on 1/23/2025 in #support-community
convex auth user creation
I see, so we would still need to wrap <Outlet /> with <Authenticated /> to refresh the auth token?
Actually TanStack Router is client side router so beforeLoad also runs on client side from my understanding, so I think this approach could also work with Taurii technically.
88 replies