remix: [Bug]: "Prop `href` did not match". when using tailwindcss

Which Remix packages are impacted?

  • remix (Remix core)
  • create-remix
  • @remix-run/architect
  • @remix-run/cloudflare-workers
  • @remix-run/dev
  • @remix-run/express
  • @remix-run/netlify
  • @remix-run/node
  • @remix-run/react
  • @remix-run/serve
  • @remix-run/server-runtime
  • @remix-run/vercel

What version of Remix are you using?

1.0.6

What version of Node are you using? Minimum supported version is 14.

16.13.1

Steps to Reproduce

  1. create remix app.
  2. add tailwindcss from this guide https://remix.run/docs/en/v1/guides/styling#tailwind.
  3. add tailwind.css to app/root.tsx.
import styles from './tailwind.css'

export function links() {
	return [{ rel: 'stylesheet', href: styles }]
}
  1. start dev server

Expected Behavior

No warning message appears on browser console

Actual Behavior

This warning message appears on console

Warning: Prop `href` did not match. Server: "/build/_assets/tailwind-DGHXG3YF.css" Client: "/build/_assets/tailwind-JUD5S62U.css"
    at link
    at Links (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:3042:7)
    at head
    at html
    at Document (http://localhost:3000/build/root-WYXGDNDR.js:55:3)
    at App
    at RemixCatchBoundary (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:1476:10)
    at RemixErrorBoundary (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:1415:5)
    at RemixRoute (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:2905:3)
    at Routes (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:2888:7)
    at Router2 (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:457:21)
    at RemixCatchBoundary (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:1476:10)
    at RemixErrorBoundary (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:1415:5)
    at RemixEntry (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:2785:12)
    at RemixBrowser (http://localhost:3000/build/_shared/chunk-KOXENSZ3.js:3262:42)

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 7
  • Comments: 24 (6 by maintainers)

Commits related to this issue

Most upvoted comments

The strangest thing is that if you change the file name of tailwind.css, the error disappears.

-"dev:css": "tailwindcss -o ./app/tailwind.css --watch",
+"dev:css": "tailwindcss -o ./app/tailwindy.css --watch",

Any time you change your dependent file (.css), you’ll get this error. Mainly because the esbuild version used by Remix isn’t properly generating the filename hash for the importing file.

root-abc123.js imports style-def456.css

after changing css file, it creates a file named style-xyz789.css, which is imported in root.js correctly, but the filename is the same root-abc123.js (same filename, different content). Since these files are cached forever, your server side (which uses the new css) and your client (which is still using the old version… since it’s been cached) is out of sync.

With that being said, the bug is fixed in latest esbuild, so hopefully it will be included in next remix release.

This is tracked and fixed in this esbuild issue https://github.com/evanw/esbuild/issues/1957

It looks like a bug in esbuild… looked up docs for Entry Names:

[hash] This is the content hash of the output file, which can be used to take optimal advantage of browser caching. Adding [hash] to your entry point names means esbuild will calculate a hash that relates to all content in the corresponding output file (and any output file it imports if code splitting is active). The hash is designed to change if and only if any of the input files relevant to that output file are changed.

https://esbuild.github.io/api/#entry-names

I was able to repro even for static CSS file. Basically anytime you import an asset that generates a hash and you change that imported file, the bundle contains the new hash, but the route filename hash stays the same… definitely not what the docs say (which is it should have a hash of the output file, including all imports).

Prop `href` did not match. Server: "/build/_assets/index-A3ETFPZR.css" Client: "/build/_assets/index-NLZIILKN.css"
import indexCss from '~/styles/index.css'

export const links: LinksFunction = () => [
  { rel: 'stylesheet', href: indexCss },
]
// build-old/routes/index-SXB245SW.js
var styles_default = "/build/_assets/index-NLZIILKN.css";

// build/routes/index-SXB245SW.js  (Same file hash)
var styles_default = "/build/_assets/index-A3ETFPZR.css";

I think I figured out why we’re getting the error Warning: Prop href did not match. Server: "/build/_assets/tailwind-UF2CZ54Y.css" Client: "/build/_assets/tailwind-GCIKYTPB.css

It looks like when esbuild compiles the route file, it generates the hash based on the contents of the original source, not the bundled output. So if your root route imports the tailwind.css file from app, the source never changes. But the tailwind URL will change and will generate a bundle with the new file path, but with the SAME root hash! And since the /public/build folder is immutable, it will never download the new root file. 😱 The only way to work around this in dev, is to disable caching in your dev tools. But you can’t rely on that on production.

To reproduce, have a clean /public/build folder. Start your app and let it build. Then rename /public/build to /public/build-old. Now add a new Tailwind class to your route and save. Remix will rebuild, but if you compare both root.js files (build and build-old), you’ll see the filename stays the same even though the import for tailwind.css has changed.

// build-old/root-AURVXRN.js
// app/tailwind.css
var tailwind_default = "/build/_assets/tailwind-A6AN5OWU.css";

// build/root-AURVXRN.js. NOTE Same filename
// app/tailwind.css
var tailwind_default = "/build/_assets/tailwind-K3O5UDQR.css";

Not sure if this is an esbuild bug (it doesn’t really say whether the hash is dependent on the source contents or the bundled contents).

it’s correct, although one more thing : it happened again to me even with the workaround. deleting .cache didn’t solve anything that time. but when I hard refresh my page, everything went fine again : I guess it was a browser cache “issue” !

The strangest thing is that if you change the file name of tailwind.css, the error disappears.

-"dev:css": "tailwindcss -o ./app/tailwind.css --watch",
+"dev:css": "tailwindcss -o ./app/tailwindy.css --watch",

this works!

I don’t know enough what’s happening here to actually help fix this but the workaround I’ve been using is…

Update package.json:

"dev:css": "tailwindcss -o ./public/tailwind.css --watch",

Then update root.jsx:

export function links() {
	return [{ rel: "stylesheet", href: "/tailwind.css" }];
}

In Dev Tools, Network Tab image

seeing this issue a lot. hard refresh in dev fixes it.

try move tailwind.css into app/styles by use command

"dev:css": "tailwindcss -o ./app/styles/tailwind.css --watch"

I confirm this issue, however, it seems more like a cache issue only. For anyone come across this issue, deleting .cache folder and re-run the server will fix the issue.

There is indeed this problem. Turning on disable cache can temporarily eliminate the warning.