router: Slow TS performance
Describe the bug
I have a fairly small project (it will grow), right now there are about ~20 pages, but I am already facing some performance issues after including the router package, mainly in the IntelliSense and auto-complete. After some investigations, I have narrowed down part of the issue to be that for every component I use navigate from useNavigate, I get a bad compilation time for that file.
I have identified this using --generateTrace flag for tsc, it points to every single one of these files to the particular line of when the navigate(...) method is being used.
npx @typescript/analyze-trace trace
Hot Spots
├─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/policiesaddedit/policiesaddedit.tsx (1050ms)
├─ Check file /users/me/dev/project/packages/web/ui/src/components/sidebar/sidebar.tsx (1001ms)
│ └─ Check deferred node from (line 70, char 9) to (line 82, char 16) (979ms)
│ └─ Check expression in /users/me/dev/project/applications/web/dashboard/src/router/index.tsx from (line 84, char 24) to (line 84, char 30) (651ms)
│ └─ Check expression from (line 71, char 16) to (line 77, char 3) (651ms)
│ └─ Check expression from (line 71, char 27) to (line 77, char 2) (649ms)
│ └─ Check expression from (line 72, char 5) to (line 72, char 14) (642ms)
│ └─ Check expression from (line 32, char 19) to (line 64, char 3) (626ms)
│ └─ Check expression from (line 32, char 41) to (line 64, char 2) (626ms)
│ └─ Check expression from (line 34, char 5) to (line 63, char 7) (444ms)
│ └─ Check expression from (line 34, char 36) to (line 63, char 6) (435ms)
│ └─ Check expression from (line 35, char 9) to (line 62, char 11) (435ms)
│ └─ Check expression from (line 35, char 37) to (line 62, char 10) (430ms)
├─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/signup/index.tsx (814ms)
│ └─ Check deferred node from (line 54, char 9) to (line 223, char 16) (735ms)
│ └─ Check expression from (line 55, char 13) to (line 58, char 44) (732ms)
│ └─ Check expression from (line 57, char 22) to (line 57, char 46) (731ms)
│ └─ Check expression from (line 57, char 23) to (line 57, char 45) (731ms)
│ └─ Compare types 137362 and 137370 (731ms)
│ └─ Check expression from (line 39, char 20) to (line 48, char 19) (731ms)
│ └─ Check expression from (line 39, char 20) to (line 45, char 23) (730ms)
│ └─ Check expression from (line 39, char 20) to (line 44, char 96) (730ms)
│ └─ Check expression from (line 44, char 23) to (line 44, char 95) (730ms)
│ └─ Check expression from (line 44, char 29) to (line 44, char 95) (730ms)
├─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/login/index.tsx (795ms)
│ └─ Check deferred node from (line 57, char 9) to (line 124, char 16) (725ms)
│ └─ Check expression from (line 58, char 13) to (line 61, char 44) (719ms)
│ └─ Check expression from (line 60, char 22) to (line 60, char 46) (718ms)
│ └─ Check expression from (line 60, char 23) to (line 60, char 45) (718ms)
│ └─ Compare types 134742 and 134800 (717ms)
│ └─ Check expression from (line 34, char 20) to (line 51, char 14) (717ms)
│ └─ Check expression from (line 38, char 17) to (line 50, char 18) (716ms)
│ └─ Check expression from (line 45, char 32) to (line 45, char 113) (716ms)
├─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/apikeysedit/index.tsx (703ms)
│ └─ Check deferred node from (line 35, char 9) to (line 65, char 10) (553ms)
│ └─ Check expression from (line 41, char 20) to (line 64, char 15) (553ms)
│ └─ Check expression from (line 58, char 21) to (line 64, char 14) (537ms)
│ └─ Check expression from (line 59, char 24) to (line 63, char 19) (537ms)
├─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/accountedit/index.tsx (615ms)
│ └─ Check deferred node from (line 33, char 32) to (line 43, char 6) (516ms)
│ └─ Check expression from (line 36, char 13) to (line 36, char 62) (515ms)
│ └─ Check expression from (line 36, char 19) to (line 36, char 62) (515ms)
└─ Check file /users/me/dev/project/applications/web/dashboard/src/pages/resolversinstancesaddedit/resolversinstancesaddedit.tsx (513ms)
└─ Check deferred node from (line 55, char 9) to (line 72, char 10) (377ms)
└─ Check expression from (line 58, char 17) to (line 61, char 40) (373ms)
└─ Check expression from (line 58, char 17) to (line 61, char 25) (373ms)
└─ Check expression from (line 58, char 17) to (line 61, char 19) (373ms)
I am using the navigate like (example from SignUp):
export default function SignUpPage() {
const navigate = useNavigate({ from: '/sign-up' });
const onSubmit = useCallback(
async (data: SignUpSchemaType) => {
// auth code
.then(() => navigate({ to: '/verify-account', search: { email: data.email } }))
},
[context.auth],
);
From the example, on the line with navigate I get the hotspot from the report.
Is there anything I can do to help out the compiler for faster resolution? Maybe if there are some recommendations, it would be helpful to add on the docs site.
Your Example Website or App
/
Steps to Reproduce the Bug or Issue
- Import via
useNavigate - Use method
navigate - Slow TS compile
Expected behavior
Fast TS compile
Screenshots or Videos
No response
Platform
- OS: MacOS 2019, Intel i9, 16GB RAM
- Browser: [e.g. Chrome, Safari, Firefox]
- Version: 1.14.0
Additional context
I have may other instnaces where navigation is made via Link and those instances doesn’t seem to cause slow compilation. Maybe because they are JSX?
About this issue
- Original URL
- State: closed
- Created 5 months ago
- Reactions: 3
- Comments: 25 (12 by maintainers)
I was looking into using Link with a large number of routes. I wonder if this is the same issue I noticed.
Regarding what I looked into when tracing. I found possibly something interesting:
RelativeToPathAutoComplete has SplitPaths which splits all paths regardless as a defaulted type parameter. If you make SplitPaths inline so it’s only evaluated when it’s needed and then when the ‘from’ prop is not specified (TFrom is defaulted to string) then return all paths without splitting all the paths. This skips the crazy splitting of a large union of paths. With these changes I noticed performance improvements when not using the ‘from’ prop
I tried this with 600 odd routes and check time went from 12.69s (with excessively deep instantiation errors) to 2.49s (with no errors)
useNavigation is more complicated. It’s defaulting TFrom to all paths and this is harder to check for (not sure why it’s defaulted this way?). TS seems to checking a large union here as well. I was able to get similar performance as Link if TFrom is defaulted to string and some other odd changes
This doesn’t fix performance issues when the ‘from’ prop is specified. There might be a better way to work out the relative paths. But that requires more work and thinking how to approach it. I would maybe try to avoid parsing of huge unions and try to narrow it down
I usually try not to iterate over large unions unnecessarily and minimise checking against them.
I’m happy to contribute if you think it’s worth speeding up the non-relative path use case for now
Optimizations in this area are very welcome, so please, if you find something out, let us know. Even if those changes would be breaking, we might consider them for v2 if the TS compiler performance can be improved significantly.
I have tried to migrate to file-based routing, and the performance is even worse. For example for the file of
policiesaddeditwhat used to be~1000msnow is at~2000msHere is the experience with InteliSense experience when the file doesn’t have the Router interaction. As you can see, the suggestions are being shown as I type, immediately when i click the shortcut to show suggestions.
https://github.com/TanStack/router/assets/1628876/43456be9-229a-42f4-b381-ba46888c3cf1
Here is the experience when the component has Router interaction. Must note that this is very simple component that even here the experience is not that bad. In my
policiesaddeditcomponent it takes ~5s to create the suggestions.https://github.com/TanStack/router/assets/1628876/da5640c7-6455-4c60-9fe8-3a6dbd938552
Here it is the hotspot log:
Taking as example
policiesaddedit, here are the lines that are mentioned:I do not have any circular dependencies between the code, route definitions, and generated route tree.
Here is gist with my generated route tree: https://gist.github.com/toteto/e7d7c1fee9f9a4bda315170758c29bd5
Cool. Yeah. I noticed that. It will work. It might be worth getting tests around these types to stop those sorts of regressions
@schiller-manuel I have some improvements for relative paths as this is still using Split and slower than it could be. Should I raise another issue for this? At least relative pathing should be faster.
I raised a PR. Looking into further improvements but this is a good start I think
https://github.com/TanStack/router/pull/1202