T
TanStack•2mo ago
eastern-cyan

Landing page downloads large bundle

I've migrated an app from Next.js to TanStack Start. The app has a simple landing page and a large (1MB+) admin dashboard. Even though my landing page route doesn't import any admin code, it's still loading the entire admin bundle (=main bundle) on initial visit. What is the idiomatic way in TanStack Start to ensure the large admin bundle is only loaded when a user navigates to the protected /admin route? I'm on file routes and enabled auto code splitting already.
39 Replies
correct-apricot
correct-apricot•2mo ago
in start, auto code splitting is enabled by default however, the root route is not split off so if you use things in the root route, they will end up in the main bundle
eastern-cyan
eastern-cyanOP•2mo ago
I have a __root.tsx, which does not include any of the main bundle deps, and a index.tsx (landing page) which also does not include any of the main bundle deps, still it is loaded
correct-apricot
correct-apricot•2mo ago
can you share a complete project?
eastern-cyan
eastern-cyanOP•2mo ago
For now I don't have anything to replicate, I try to continue tinkling, as I understand, in theory it should be split - so that's enough information for the moment - thank you. My suspicion is that I'm including too much in the loader in other routes, which probably will get into the main bundle, so I'll try to also split that off. I'm seeing https://tanstack.com/router/latest/docs/framework/react/guide/automatic-code-splitting is empty, but it says "many customization options available", where can i find those in the source code?
Automatic Code Splitting | TanStack Router React Docs
[!TIP] We'll be filling in this guide soon about the wonderful world of automatic code splitting with TanStack Router and the many customization options available to you. Stay tuned! <!-Include the ba...
correct-apricot
correct-apricot•2mo ago
you can split off loaders if you want we don't do this by default that's the config schema
correct-apricot
correct-apricot•2mo ago
GitHub
router/packages/router-plugin/src/core/config.ts at main · TanStac...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
correct-apricot
correct-apricot•2mo ago
we need to document this ... it's super powerful
correct-apricot
correct-apricot•2mo ago
GitHub
router/packages/router-plugin/tests/code-splitter.test.ts at main ...
🤖 Fully typesafe Router for React (and friends) w/ built-in caching, 1st class search-param APIs, client-side cache integration and isomorphic rendering. - TanStack/router
eastern-cyan
eastern-cyanOP•2mo ago
Thank you!
correct-apricot
correct-apricot•2mo ago
if you want to contribute to the documentation , let me know cc @Sean Cassiere
deep-jade
deep-jade•2mo ago
Interesting, probably not related i have tried the groups for the react-dom but it didnt liked a lot :p my only way to push for example the react-dom-client out of the main bundle was like this:
const { hydrateRoot } = await import('react-dom/client')
const router = createRouter()

hydrateRoot(
document,
<StartClient router={router} />,
{
onRecoverableError,
onCaughtError,
}
)
const { hydrateRoot } = await import('react-dom/client')
const router = createRouter()

hydrateRoot(
document,
<StartClient router={router} />,
{
onRecoverableError,
onCaughtError,
}
)
correct-apricot
correct-apricot•2mo ago
why would you want that? you will always load react so why split it off?
deep-jade
deep-jade•2mo ago
to keep the main entry point less than 500kb or so, like the OP my main file was around 1.2mb ( not gziped ) probably paranoia 😄 But your probably right, maybe usless as it still loads on the root :p
eastern-cyan
eastern-cyanOP•2mo ago
Created an initial draft for the docs, feel free to provide feedback: https://github.com/TanStack/router/pull/4717
GitHub
Add Automatic code splitting documentation by lukstei · Pull Reque...
Adds Automatic code splitting documentation page
eastern-cyan
eastern-cyanOP•2mo ago
Disclaimer: This is mainly AI generated, I still need to play around with it to verify the contents, but on the first sight it looks good
correct-apricot
correct-apricot•2mo ago
cool, will review later. did this fix your initial issue btw?
eastern-cyan
eastern-cyanOP•2mo ago
Not really unfortunately, the main bundle gets a little smaller when also splitting the loaders, however it's still loaded with the landing page, because e.g. React is needed from the main bundle. I guess this has nothing to do with the auto code splitting feature. I believe the way to go would be to create a completely separate bundle for a specific route - but I can imagine that's not easily possible..
correct-apricot
correct-apricot•2mo ago
but why though? what ends up in the main bundle that this route does not need? I mean react is needed for hydration unless you don't want to have interactivity on that route
eastern-cyan
eastern-cyanOP•2mo ago
My app looks like follows: __root.tsx index.tsx <- landing page, only displays some basic HTML, and only needs the React dependency admin <- the admin subroutes contain the actual app, with a lot of dependencies index.tsx editor.tsx ... In the main bundle, as I see it now, are all modules which are used by more than one route, including React plus all the dependencies of the admin routes. Because the landing page needs react, it is forced to load the main bundle. Could be also I'm misunderstanding something, I'm completely new to this. I also tried to put React into a separate chunk (using https://rollupjs.org/configuration-options/#output-manualchunks), but it seems like the root route still wants to preload the main bundle.
eastern-cyan
eastern-cyanOP•2mo ago
This is the generated HTML of the landing page, there you can see the root route wants to load the main bundle
correct-apricot
correct-apricot•2mo ago
this should still work though with autocodesplitting that's probably the issue then
In the main bundle, as I see it now, are all modules which are used by more than one route,
vite/rollup combining those modules that are used by multiple dynamic imports a reproducer would be nice to have something to play with
eastern-cyan
eastern-cyanOP•2mo ago
Okay, I tried to reproduce with a vanilla project, however I cannot get it to behave like in my project, it seems to work perfectly fine, as you pointed out. So I'm likely having an issue in my app, so I try to investigate further.. I finally found the issue: There were some route components which were exported, this messes up the code splitting. Now it finally works 🙂 So thanks for your help, I'm happy that i found the problem! Super happy with Tanstack for now, I migrated my app from Next.js this week, and so far it feels like the correct decision. The only issue I had was this huge bundle size, which was kind of hard to debug and find the issue, but at the end it was just a stupid mistake.
deep-jade
deep-jade•2mo ago
Can you share a bit what you have done?
eastern-cyan
eastern-cyanOP•2mo ago
Basically I deleted all routes but the landing page, built the app, noted down the bundle size. Then re-introduced the other routes one by one, and looked if they added code to the main bundle, and that's how I found the misbehaving routes (which added size to the main bundle instead of to their own bundle).
correct-apricot
correct-apricot•2mo ago
didnt you get a warning in the build logs? should have printed this
const warningMessage = `These exports from "${opts.filename}" are not being code-split and will increase your bundle size: ${list}\nThese should either have their export statements removed or be imported from another file that is not a route.`
console.warn(warningMessage)
const warningMessage = `These exports from "${opts.filename}" are not being code-split and will increase your bundle size: ${list}\nThese should either have their export statements removed or be imported from another file that is not a route.`
console.warn(warningMessage)
eastern-cyan
eastern-cyanOP•2mo ago
No, just checked, there is no warning
correct-apricot
correct-apricot•2mo ago
can you show me the problematic routes? you should have gotten that warning in dev mode in the browser console but it should also have printed this as a warning
eastern-cyan
eastern-cyanOP•2mo ago
No warning in the browser either
eastern-cyan
eastern-cyanOP•2mo ago
correct-apricot
correct-apricot•2mo ago
just added this to our tests and it prints the warning
stderr | tests/code-splitter.test.ts > code-splitter works > FRAMEWORK=react > SPLIT_GROUP='3-all-combined-errorComponent-separate' > should compile "virtual" for "export-named-component.tsx"
These exports from "export-named-component.tsx?component---loader---notFoundComponent---pendingComponent" are not being code-split and will increase your bundle size:
- DevAdmin
stderr | tests/code-splitter.test.ts > code-splitter works > FRAMEWORK=react > SPLIT_GROUP='3-all-combined-errorComponent-separate' > should compile "virtual" for "export-named-component.tsx"
These exports from "export-named-component.tsx?component---loader---notFoundComponent---pendingComponent" are not being code-split and will increase your bundle size:
- DevAdmin
eastern-cyan
eastern-cyanOP•2mo ago
Is the test executing a build?
eastern-cyan
eastern-cyanOP•2mo ago
correct-apricot
correct-apricot•2mo ago
maybe a vite 7 thing? i just added an export statement to one of our example projects and I see the warning in the console on which operating system are you on?
eastern-cyan
eastern-cyanOP•2mo ago
MacOS 15
correct-apricot
correct-apricot•2mo ago
this is not using rolldown right?
eastern-cyan
eastern-cyanOP•2mo ago
Correct
correct-apricot
correct-apricot•2mo ago
cc @Sean Cassiere do you see anything here that would cause the warning to not appear?
eastern-cyan
eastern-cyanOP•2mo ago
Would that be possible? If no interactivity having no react dependency loaded for one route? Is it possible to make a route not depend on the main chunk and force bundling all the dependencies with the route chunk itself? The use case for that would be the landing page, which has the dependency on the main chunk (e.g. for React). But if the main chunk gets heavier, due to some other routes dependencies, the landing page also needs to load more stuff, which is annoying to constantly watch out for.
correct-apricot
correct-apricot•2mo ago
you could conditionally not render the <Scripts> component. then the landing page would not load the main bundle (but would also not hydrate!)

Did you find this page helpful?