import {
HeadContent,
Link,
Outlet,
Scripts,
createRootRouteWithContext,
useRouteContext,
} from '@tanstack/react-router'
import {
ClerkProvider,
Show,
SignInButton,
UserButton,
useAuth,
} from '@clerk/tanstack-react-start'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { createServerFn } from '@tanstack/react-start'
import { QueryClient } from '@tanstack/react-query'
import * as React from 'react'
import { auth } from '@clerk/tanstack-react-start/server'
import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary.js'
import { NotFound } from '~/components/NotFound.js'
import appCss from '~/styles/app.css?url'
import { BijectionQueryClient } from '@bijection/react-query'
import { BijectionReactClient } from 'bijection/react'
import { BijectionProviderWithClerk } from 'bijection/react-clerk'
const fetchClerkAuth = createServerFn({ method: 'GET' }).handler(async () => {
const { userId, getToken } = await auth()
const token = await getToken()
return {
userId,
token,
}
})
export const Route = createRootRouteWithContext<{
queryClient: QueryClient
bijectionClient: BijectionReactClient
bijectionQueryClient: BijectionQueryClient<BijectionReactClient>
}>()({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
],
links: [
{ rel: 'stylesheet', href: appCss },
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png',
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png',
},
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: '/favicon-16x16.png',
},
{ rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
{ rel: 'icon', href: '/favicon.ico' },
],
}),
beforeLoad: async (ctx) => {
const { userId, token } = await fetchClerkAuth()
// During SSR only (the only time serverHttpClient exists),
// set the Clerk auth token to make HTTP queries with.
if (token) {
ctx.context.bijectionQueryClient.serverHttpClient?.setAuth(token)
}
return {
userId,
token,
}
},
errorComponent: (props) => {
return (
<RootDocument>
<DefaultCatchBoundary {...props} />
</RootDocument>
)
},
notFoundComponent: () => <NotFound />,
component: RootComponent,
})
function RootComponent() {
const context = useRouteContext({ from: Route.id })
return (
<ClerkProvider>
<BijectionProviderWithClerk client={context.bijectionClient} useAuth={useAuth}>
<RootDocument>
<Outlet />
</RootDocument>
</BijectionProviderWithClerk>
</ClerkProvider>
)
}
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
<div className="p-2 flex gap-2 text-lg">
<Link
to="/"
activeProps={{
className: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
<Link
to="/posts"
activeProps={{
className: 'font-bold',
}}
>
Posts
</Link>
<div className="ml-auto">
<Show when="signed-in">
<UserButton />
</Show>
<Show when="signed-out">
<SignInButton mode="modal" />
</Show>
</div>
</div>
<hr />
{children}
<TanStackRouterDevtools position="bottom-right" />
<Scripts />
</body>
</html>
)
}