T
TanStack2mo ago
optimistic-gold

Client-only router context

With TanStack Router (without Start) we can pass context from a React Context by putting the <RouterProvider> inside the React Context provider and passing the context prop. https://tanstack.com/router/latest/docs/framework/react/guide/router-context#how-about-using-react-contexthooks But in TanStack Start there is <StartClient> instead of <RouterProvider> and it doesn't take context. My use case right now is in migrating a Router app to Start so that we can get some dynamic SSR injection into the root route but have all other routes disable SSR with the new selective SSR. Our app's loaders can't be isomorphic so what I am doing now is creating a layout under the root route to initialize the client-side context values but these get recreated every time that beforeLoad runs which is every navigation. This is different than the Router case where we would initialize the router with those context values so the initialization wouldn't be in a loader. I might be missing an obvious pattern to achieve what I need. I'm sure there is a way to also just have client-only initialization and access those in client-only loaders such as initializing them in client.tsx but it feels less "elegant" than the context pattern in Router.
15 Replies
wise-white
wise-white2mo ago
why can't they be isomorphic? you would need to disable SSR then for those routes I guess?
optimistic-gold
optimistic-goldOP2mo ago
Our auth solution is client-centric and and needs client-side initialization and reads local storage We are working towards moving to a different auth solution (Clerk) that would enable auth in SSR but for now not sure what the best solution is I'm using defaultSsr: false and only have ssr: true on the root route. But yes all of our other routes don't have SSR enabled
wise-white
wise-white2mo ago
so then don't SSR those routes loaders won't run on the client then
optimistic-gold
optimistic-goldOP2mo ago
Yes, my question is more about if there's a way to seed router context client-only. So if I have an Auth instance that can only be provided starting on the client is there a way to get that into the router context so it can be used by loaders? Right now I am initializing it in a beforeLoad in a pathless layout but this gets re-run for every navigation.
wise-white
wise-white2mo ago
so do you need this context to live I react land? btw you should be able to distinguish the initial beforeLoad invocation from the others using the cause that is passed in we are also working on adding new lifecycle methods that would allow you to accomplish exactly what you need, one would be called with the same caching semantics as loader not there yet though feeding something into StartClient feels wrong as this would break the "server has the same data as client on root" premise
optimistic-gold
optimistic-goldOP2mo ago
The values of the context do not have to be inside React, but perhaps it's best that it is injectable into the router context. I imagine there is a solution where in client.tsx we just have:
export const auth = new Auth();
export const auth = new Auth();
and then just import { auth } from "@/client.tsx" for use in the client loaders. Yes I think doing that injection through StartClient would not make sense, but with selective SSR (especially because of the more-restrictive inheritance) perhaps there could be an API for "this route and its children are not SSR'd, so we can inject client-only context"
wise-white
wise-white2mo ago
btw you can also do ssr:false on the root route and put your html shell into shellComponent then you can avoid the pathless layout in between
optimistic-gold
optimistic-goldOP2mo ago
How can I get a value coming from the server in that case? For example if I want to pass a value from the server I would do it through the context and keep ssr: true on the root.
wise-white
wise-white2mo ago
how is that value "coming from the server"?
optimistic-gold
optimistic-goldOP2mo ago
It's dynamic depending on the environment variables on the server process (configuration of our build and deploy pipeline means we can't just rebuild the app with different environment variables for different environments, so the environment variables are provided in the process.env at runtime)
wise-white
wise-white2mo ago
you could just use dehydrate and hydrate then
optimistic-gold
optimistic-goldOP2mo ago
This is a TanStack Query API?
wise-white
wise-white2mo ago
no router
optimistic-gold
optimistic-goldOP2mo ago
Ah ok, thanks! Will take a look - appreciate the discussion I'm finding that with defaultSsr: false and no ssr: true in __root.tsx that there is some invisible error and no page gets delivered to the browser. I suppose this is a pretty uncommon case for one using TanStack Start but was wondering if this is the intended behavior. If I add ssr: true to the root route I can keep child routes with SSR disabled just fine.
wise-white
wise-white2mo ago
it's not documented yet, but you can use shellComponent on the root and put your <html> shell in there that shell MUST always be rendered https://github.com/TanStack/router/blob/main/examples/react/start-basic/src/routes/__root.tsx#L62

Did you find this page helpful?