monorepo setup
what's the recommended setup for working in a monorepo, given that TanStack Router uses module augmentation?
we have a monorepo with a composite setup, so each package emits its own
.d.ts files. Now our setup is roughly like this:
now app-one has the routes, the generated route-tree and the module augmentation of @tanstack/react-router. but inside libs/some-shared-lib, I would like to have a component that renders a <Link> from the router. As far as I can see, the module augmentation is not "seen" inside that lib, so I don't have type-safety for anything there.
How could we solve this?41 Replies
correct-apricotOP•2y ago
would it maybe work to have the generation output multiple
routeTree.gen.ts files - one per package ?flat-fuchsia•2y ago
We can discuss more here. Apart from the cyclic dependency that only imports the type. You could use
paths. Maybe that will help because only TS knows about the dependency, not your bundler.
I was thinking about a separate package like packages/routes where you would run the generator and both the app and shared lib would import from it. You would need to fill in the component and loader with update in app. But I'm not sure if I like this solution either because you might end up pushing all your code into packages/routes in the end and I don't think it will pick up the loader types actually
Or we need some new concept in router. Like an abstract router but this might be beyond my API design skillsabsent-sapphire•2y ago
I found the following relevant SO threads:
one uses
pathsals @Chris Horobin suggested:
https://stackoverflow.com/a/75699722/678093Stack Overflow
Material UI module augmentation across multiple packages in a monorepo
I am using lerna to create multiple UI packages.
I am augmenting @material-ui/styles in package a to add more palette and typography definitions.
I am able to get the new types in package a.
Packag...
absent-sapphire•2y ago
the other one imports the file that contains the
declare module in the other packages:
https://stackoverflow.com/a/69907717/678093Stack Overflow
Share typescript module with every package in a monorepo
I have a monorepo of react components that are built using styled-components, getting their themes from a theme provider.
declare module 'styled-components' {
export interface DefaultTheme {
correct-apricotOP•2y ago
I found both the answers on SO too, but they weren't really helpful :/
I also wanted to explore the separate package, but couldn't get it to work. The routeTree.gen also does module augmentation and it imports all the routes, so the shared package needs to know about all the route files, and likely even create the router instance itself and export it
absent-sapphire•2y ago
why did both not work for you? can you share some more insights here?
can this be solved by generating different output in the router generator? if yes, what would you need?
correct-apricotOP•2y ago
A coworker mentioned yesterday that
paths can work and we'll talk about it today. If we get this done I'd like to add a file-based monorepo example to the docsinland-turquoise•2y ago
That would be awesome. I had previously tinkered with using file-based routing in both the root app and the underlying lib, which "worked" (rendered the right components with correct nesting) except I wasn't able to figure out how to attach the route tree in the library to a branch of the route tree in the root, which meant the current URL was never updated, behavior was a bit odd, and there were type errors since the root app wasn't aware of the sub app routes.
Being able to import and attach route trees to existing route trees to extend them send like it should be possible, but I wasn't able to get the types to line up.
useful-bronze•2y ago
Did anyone ever figure out a viable, type-safe monorepo solution for TSR?
correct-apricotOP•2y ago
Yes, I did 🙂 will try to write about it soon
correct-apricot•13mo ago
@TkDodo 🔮 Any progress on this? Or a working solution/example that you could share? We're facing the same issue and can't figure it out properly.
correct-apricotOP•13mo ago
Yes, there's an open source repro by @Nicolas Beaussart
correct-apricotOP•13mo ago
GitHub
GitHub - beaussan/tanstack-router-react-mono
Contribute to beaussan/tanstack-router-react-mono development by creating an account on GitHub.
correct-apricot•13mo ago
Thank you for sharing 🙏 I wanted to avoid this solution because of the giant routeMap - we have ~1000 routes so I don't want to import all the components and loaders in 1 place. You also lose the ability to click-through from the route definition directly to the component.
Another option would be for each domain/feature to register its own components 🤔 I'm just wondering if it's somehow possible for the route package to depend on the feature packages and provide the types back to them without circular dependencies... Could virtual routes help here or it doesn't change anything type-wise?
absent-sapphire•13mo ago
we have those examples also integrated in our repo
see https://tanstack.com/router/latest/docs/framework/react/examples/router-monorepo-react-query
TanStack | High Quality Open-Source Software for Web Developers
Headless, type-safe, powerful utilities for complex workflows like Data Management, Data Visualization, Charts, Tables, and UI Components.

correct-apricot•13mo ago
@Manuel Schiller I saw that one as well, but it's the same solution - define a skeleton router and dynamically inject all components etc, which doesn't really scale for hundreds of routes 🫤
correct-apricotOP•13mo ago
@honzk do you have a better suggestion on how to avoid the circularity? I mean if you expect to have routes that render components where the components depend on the route definition, and you have that in different packages, that is a circularity you have to break.
my suggestion is to break it up between router and component: to create a router, with everything it needs to infer the types (paths, loaders, search param schemas, ...) but without components, then later go into your app (that needs to depend on everything anyways) and add the components, likely with lazy loading.
we have ~1000 routes so I don't want to import all the components and loaders in 1 placewhy loaders? only components need to be imported imo. And right now, this is the best trade-off I have to offer. If you know something better, I'd be happy to hear it 🙂
correct-apricot•13mo ago
If you want to trade flexibility for less type safety, you could imagine a plugin like architecture where you define the connected component to a bunch of route, defined in a plugin close to the feature libraries, and then from the list of plugins update the router, however this has a huge drawback: you could have a route without components, because you don't enforce route = component, and you will have no way of knowing it
correct-apricot•13mo ago
Loaders might contain domain-specific logic that lives next to the components. Even without considering loaders, this becomes quite messy.
That being said, I don’t have any better solution, I’m just trying to figure out the best way to do this in scale.
harsh-harlequin•13mo ago
We have a similar monorepo setup. Currently handling this by adding a “export routes” command to the package that contains the router. “Export routes” makes a type only build and outputs the types to the root of the monorepo. Then individual packages that need this add the root file to their includes config
Every package that includes the outputted types can just import tanstack router and have it work as expected.
jolly-crimson•11mo ago
@DavidDavid Do you mind sharing this setup with us?
@Nicolas Beaussart I had a look at your example repo, thank you for taking the time to create it! While i appreciate the effort, I think the setup introduces a lot of complexity, basically to please Typescript and have type-safe routes. Do you think this added complexity is a worthwhile trade-off? I’ve been wrestling with this problem for the past few days but haven’t been able to find an alternative solution.
I have the following setup:
I created the router library purely to have type-safety and to export a custom Link component.
correct-apricot•11mo ago
This is like the other example I've contributed to the TanStack router docs https://tanstack.com/router/latest/docs/framework/react/examples/router-monorepo-simple
Basically, this works as it is, however if you need to share query options between the router and the dashboard lib in your example, that's where the data access kind of library make sense, or if you only use loader that works great
To answer the question above "do you think it's worth the trade off?" it depends on the size :
If you have few teams contributing to the monorepo, maybe not, and maybe that's fine for you to expose query options directly from the router lib, but if you start having 100+ contrib on the app / repo, then having the data access library make sense in terms of boundaries and contribution model, because at the end, a monorepo is a solution to a organisational issue
React TanStack Router Router Monorepo Simple Example | TanStack Rou...
An example showing how to implement Router Monorepo Simple in React using TanStack Router.
jolly-crimson•11mo ago
I was actually more referring to the decision of having the routing definitions in one lib and the route mapping in the application, doesn't feel intuitive imo. So I was mostly questioning that trade-off, also you loose the ability of route splitting with this setup. You can ofc lazy load the components but things like pre-fetching when hovering a link don't work anymore.
The whole module augmentation thing has a huge impact on how we have to structure our mono-repo and I find that very limiting. Is this a TS limitation or TSR limitation?
correct-apricot•11mo ago
Maybe there is something we can do on the pre fetching of components on link hover when we use manually lazy in the monorepo route to component @Manuel Schiller ?
absent-sapphire•11mo ago
does it not work automatically?
correct-apricot•11mo ago
It seems like no from @jsef comments
I'll make an docs example with lazys and we shall see! I'll let you know, and it it dosn't work we can start from there
jolly-crimson•11mo ago
Well, it might be that I didn't configure it properly. This is what I did:
Do I have to use the
.lazy suffix in the file name of the route and use createLazyFileRoute?correct-apricot•11mo ago
Hey, I've riff off a few ideas, and this is what I cam up with: https://github.com/TanStack/router/pull/3244
Feature libs expots a lazy route (
createLazyRoute), that is then linked to the actual route on the end app, this supports lazy loading of pages with also pre loading with mouse hover intent
Cc @TkDodo 🔮 , I'm sure this will peak your interest, no more route.update in this caseGitHub
docs: add lazy loaded monorepo example by beaussan · Pull Request #...
Key files to look at:
Where we plug the lazy routes with the routes
Example exposed route from a feature library
Re exported types and functions from the router to have type augmentation
correct-apricotOP•11mo ago
looks good, but is conceptually the same, no? We're just trading a call to
route.update for route.lazy(component), and we also need the manual routerMapcorrect-apricot•11mo ago
Nearly, one thing differs: with
route.lazy() the component is pre loaded on link hover vs after click, plus it allow to colocate not foud, error, pending as one configcorrect-apricotOP•11mo ago
oooh, yeah that's nice
correct-apricot•8mo ago
Hi @honzk , I am trying to setup tanstack router in a monorepo and I have same requirements as you. Have you found some viable solution?
deep-jade•8mo ago
If I'm understanding correctly, to use with a monorepo setup we have to manually add each route to the routermap?
correct-apricot•7mo ago
Yes, that's the recommended way.
I really like TanStack Router, but this monorepo setup isn't good from the developer experience standpoint. I hope there will be a better way in the future
correct-apricot•7mo ago
I have a experiment with Nx sync generator to keep this map up to date based on lazy route exposed by featue libraries https://github.com/beaussan/tanstack-router-sync-experiment
I need to test it on a larger scale repo, but that removed one part of the setup
(based on https://nx.dev/concepts/sync-generators )
GitHub
GitHub - beaussan/tanstack-router-sync-experiment
Contribute to beaussan/tanstack-router-sync-experiment development by creating an account on GitHub.
Nx
Sync Generators
Learn how to use Nx sync generators to maintain repository state and update configuration files based on the project graph before tasks are run.
extended-salmon•7mo ago
Hi im trying to setup a monorepo with tanstack/router and nestjs as backend as well.
The only thing im actually interested in is to have the routes shared between frontend and backend.
I thought about this setup
basically the frontend generates and consumes the routeTree from libs/routes and the backend can consume the routes just to have route autocompletion.
This way we can create emails/notifications etc in the backend on existing routes and we don't need to guess them.
How can we implement this?
jolly-crimson•6mo ago
Hey @Nicolas Beaussart , isn't this a bit overengineered considering the actual benefits of it? I got used to updating the map manually since it is strongly typed and would get a type error anyway when building the app.
correct-apricot•6mo ago
I don't have an off the shelf solution yet, just a poc that is public
For a small team maybe, but I'm working with a team of 150+ eng on the same app, so automation like that are kinda needed 😅
jolly-crimson•6mo ago
Oh yeah, that makes sense :p I assume you are using NX?
correct-apricot•6mo ago
Yup, heavily! Couldn't run the frontend platform without it
stormy-gold•6mo ago
no. never.
if you think it's overengineering to use nx, then don't do a monorepo.
the moment you go monorepo, there are so many things you need to nail down and put gaurdrails around.
there's a difference between:
- yes my naive approach works because it doesn't explode
- i want to scale onboarding other team members and don't want to spend my life doing PR reviews on stuff that could have been systemised and automated
