You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add CSS Modules support by creating a .module.css file and importing it into a React component.
Run the server and open the app in a browser.
Observe that the styles are applied only after the client-side hydration, causing a flash of unstyled content (FOUC).
Expected behavior
I would like to see a working example or documentation on how to properly configure TanStack Router with CSS Modules and SSR.
The desired behavior is to have styles included in the server-rendered HTML, so there is no flash of unstyled content (FOUC).
In addition to this. on build, the CSS is not referenced by cloudflare-pages. I also ran a build using the node-server preset and it also has the same behavior.
@gioruu, your example works because you use the URL directly in the root. However, it doesn't make sense; we need to have the ability to write CSS and place it next to a component using CSS Modules. Neither CSS Modules nor imports work properly within a component.
It works as expected in other frameworks like Next.js.
Reacted by ra8ga, Rubén, Joshua Ji, Carlos, Anton Stoychev, v1rtl, Hossein Alimadad, alesdevux, Sean Lynch, trompx and 6 more
@j-mcfarlane, not sure about his repo but when i added the the ?url it worked for me and it started including it in my build
import { AppProvider } from '@shopify/polaris'
import POLARIS_CSS from '@shopify/polaris/build/esm/styles.css?url'
import enTranslations from '@shopify/polaris/locales/en.json'
import type { ReactNode } from 'react'
import CLIENT_CSS from '../client.css?url'
import { QueryProvider } from '../providers/QueryProvider'
I previously posted in this thread. Since then, our team has been using CSS modules with TSS, so I want to try and bring some of our understanding and maybe help others fix their issue:
CSS modules are working, and are split per route in production.
CSS modules are not files during dev mode, they are style tags that are dynamically added, so you are seeing FOUC. That's normal.
You can see FOUC in production if you haven't configured your hosting provider (netlify, vercel, etc.) to cache the CSS files. Before we configured that, we were seeing FOUC.
netlify.toml:
[[headers]]
# Files inside /assets/ use hash-based names, so we can cache them foreverfor = "/assets/*"
[headers.values]
cache-control = "public, max-age=31536000, immutable"
.vercel/output/config.json (we use our own CI to build):
{
"version": 3,
"overrides": {},
"routes": [
{
"src": "/assets/(.*)",
"headers": {
"cache-control": "public, max-age=31536000, immutable"
}
},
{
"handle": "filesystem"
},
{
"src": "/(.*)",
"dest": "/<COPY OF dist/server INTO .vercel/output/functions where index.mjs just re-exports dist/server.js>.func"
}
]
}
You can see FOUC in production if you haven't configured your hosting provider (netlify, vercel, etc.) to cache the CSS files. Before we configured that, we were seeing FOUC.
But thats not really solving the issue, does it? It only ensures the files can be loaded faster and therefore have no flicker if i understand your configs correctly.
But thats not really solving the issue, does it? It only ensures the files can be loaded faster and therefore have no flicker if i understand your configs correctly.
I'm sharing what helped our team. The FOUC never happened on first page load for us. It only happened when navigating from the already loaded page to another or back from a second page.
We found out that the browser is discarding the preloaded (on hover) CSS because there was no cache settings.
Adding the configuration above solved the issue for us. It is very easy to test out at least the netlify config and see if it works for you.
We've never experienced a different CSS name getting generated for the server bundle and for the client bundle. Regarding this topic I'm assuming that something goes wrong with the hashing of the filename, or the file name includes random characters where it should actually be hash of the contents.
On my side, I have a page transition with motion AnimatePresence, that fades the previous page. The styles of the previous page are removed before the page faded out, so I get the FOUC. It's a small website so as a temporary "very disgusting" hack, I added an empty .fouc class to all the CSS modules and imported them on a Layout component common to all pages -> no more FOUC, but suboptimal styles loading.
But as a new user, this is one of the first things I hit within minutes when not using Tailwind/static CSS. It looks pretty rough, and it immediately lowers confidence that the rest of the framework is worth the try. Subjectively of course.
Reacted by KyxngminKim, nbr, bashan-litt, Micah Allen, Ray Zaranza, Changsong Tai, Stefan Girlich, Anton Fedchenko, hamitkera, blagodus and 2 more
#3023 (comment) helped me identify the cause of the FOUC I was experiencing in my production build — thank you!
One thing I noticed is that FOUC can be reproduced by enabling "Disable cache" in DevTools. It doesn't occur on the initial page load, but rather during client-side navigation (e.g., navigating to another page and then pressing the back button). With cache enabled, styles load correctly without any flash.
Even if this is the expected behavior, I think it would be worth addressing — having FOUC during navigation with cache disabled isn't a great DX and can make it difficult to distinguish between real styling issues and caching behavior.
Add CSS Modules support by creating a .module.css file and importing it into a React component.
Run the server and open the app in a browser.
Observe that the styles are applied only after the client-side hydration, causing a flash of unstyled content (FOUC).
Expected behavior
I would like to see a working example or documentation on how to properly configure TanStack Router with CSS Modules and SSR. The desired behavior is to have styles included in the server-rendered HTML, so there is no flash of unstyled content (FOUC).
Screenshots or Videos
No response
Platform
OS: [e.g. macOS, Windows, Linux]
Browser: [e.g. Chrome, Safari, Firefox]
Version: [e.g. 91.1]
Additional context
No response
It tooke me 48hr to fucking fix this tanstack start fouc issue
const config = defineConfig({
plugins: [
devtools(),
nitro({
// Cache hashed assets forever — fixes FOUC during client-side navigation.
// Without this the browser re-fetches CSS on each navigation and can
// show unstyled content while the request is in-flight.
// See: https://github.com/TanStack/router/issues/3023#issuecomment-2559735249
routeRules: {
// globals.css has no hash (stable name) — must-revalidate so browsers check for updates
'/assets/globals.css': {
headers: { 'cache-control': 'public, max-age=0, must-revalidate' },
},
// All other hashed assets can be cached forever
'/assets/**': {
headers: { 'cache-control': 'public, max-age=31536000, immutable' },
},
},
}),
viteTsConfigPaths({
projects: ['./tsconfig.json'],
}),
tailwindcss(),
tanstackStart(),
viteReact({
babel: {
plugins: ['babel-plugin-react-compiler'],
},
}),
],
build: {
// Raise warning threshold — some vendor bundles (Mermaid, Excalidraw)
// are intentionally large and can't be split further without dynamic import
chunkSizeWarningLimit: 600,
rollupOptions: {
output: {
manualChunks,
// globals.css is processed twice during build (once via ?url import for SSR manifest,
// once as a transitive dep of @blocknote/shadcn) producing two different hashes.
// A stable filename ensures the SSR manifest URL always matches the deployed file.
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'globals.css') {
return 'assets/globals.css'
}
return 'assets/[name]-[hash][extname]'
},
},
},
},
})
export default config
Activity
roboncode commentedon Dec 21, 2024
In addition to this. on build, the CSS is not referenced by
cloudflare-pages. I also ran a build using thenode-serverpreset and it also has the same behavior.app.config.tsclient.tsxgioruu commentedon Dec 21, 2024
Having the same flash of unstyled content (FOUC) problem. Waiting for a Messiah.
gioruu commentedon Dec 21, 2024
This worked:
Add a '?url' to the css import:
import CSS from '../app.css?url'Add this line to the Route object head:
links: [{ rel: 'stylesheet', href: CSS }]Full code:
"@tanstack/start": "^1.91.3",
roboncode commentedon Dec 23, 2024
Thx @gioruu. I verified this works as well.
nikolayemrikh commentedon Dec 23, 2024
@gioruu, your example works because you use the URL directly in the root. However, it doesn't make sense; we need to have the ability to write CSS and place it next to a component using CSS Modules. Neither CSS Modules nor imports work properly within a component.
It works as expected in other frameworks like Next.js.
nikolayemrikh commentedon Dec 23, 2024
@SeanCassiere could you please reopen it? Not everyone use tailwind.
roboncode commentedon Dec 24, 2024
@j-mcfarlane, not sure about his repo but when i added the the ?url it worked for me and it started including it in my build
IRlyDontKnow commentedon Dec 24, 2024
That's because you're not using CSS Modules in your example ;)
53 remaining items
antitoxic commentedon Dec 1, 2025
I previously posted in this thread. Since then, our team has been using CSS modules with TSS, so I want to try and bring some of our understanding and maybe help others fix their issue:
netlify.toml:.vercel/output/config.json(we use our own CI to build):wijonja commentedon Dec 1, 2025
thank you @antitoxic. i'll give that a go
dunklesToast commentedon Dec 1, 2025
But thats not really solving the issue, does it? It only ensures the files can be loaded faster and therefore have no flicker if i understand your configs correctly.
antitoxic commentedon Dec 2, 2025
I'm sharing what helped our team. The FOUC never happened on first page load for us. It only happened when navigating from the already loaded page to another or back from a second page.
We found out that the browser is discarding the preloaded (on hover) CSS because there was no cache settings.
Adding the configuration above solved the issue for us. It is very easy to test out at least the netlify config and see if it works for you.
We've never experienced a different CSS name getting generated for the server bundle and for the client bundle. Regarding this topic I'm assuming that something goes wrong with the hashing of the filename, or the file name includes random characters where it should actually be hash of the contents.
trompx commentedon Dec 2, 2025
On my side, I have a page transition with motion AnimatePresence, that fades the previous page. The styles of the previous page are removed before the page faded out, so I get the FOUC. It's a small website so as a temporary "very disgusting" hack, I added an empty .fouc class to all the CSS modules and imported them on a Layout component common to all pages -> no more FOUC, but suboptimal styles loading.
KoziKim commentedon Dec 20, 2025
I’m looking at TanStack Start for a side project, but the FOUC with CSS Modules is pretty concerning.
Is there any update on this?
schiller-manuel commentedon Dec 20, 2025
@KoziKim the FOUC during dev you mean?
ViliamKopecky commentedon Dec 20, 2025
@schiller-manuel Yes, dev mode only.
I get why that feels unimportant.
But as a new user, this is one of the first things I hit within minutes when not using Tailwind/static CSS. It looks pretty rough, and it immediately lowers confidence that the rest of the framework is worth the try. Subjectively of course.
schiller-manuel commentedon Dec 20, 2025
we will fix this for sure, just not highest priority when compared to runtime bugs
rhyek commentedon Dec 20, 2025
@schiller-manuel some comments here from multiple users are saying this is happening for them in prod. Is this not correct?
schiller-manuel commentedon Dec 20, 2025
@rhyek i havent seen a reproducer for this. do you have one?
feat: collect styles in dev
myamaak commentedon Feb 25, 2026
#3023 (comment) helped me identify the cause of the FOUC I was experiencing in my production build — thank you!
One thing I noticed is that FOUC can be reproduced by enabling "Disable cache" in DevTools. It doesn't occur on the initial page load, but rather during client-side navigation (e.g., navigating to another page and then pressing the back button). With cache enabled, styles load correctly without any flash.
Even if this is the expected behavior, I think it would be worth addressing — having FOUC during navigation with cache disabled isn't a great DX and can make it difficult to distinguish between real styling issues and caching behavior.
chill-czar commentedon May 4, 2026
It tooke me 48hr to fucking fix this tanstack start fouc issue