question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

CSS Modules – random vs deterministic class names in production

See original GitHub issue

Firstly, many thanks to @ro-savage for implementing CSS Modules in #2285 (merged and ready for release in react-scripts@2.0 – see #3815) 🎉

I’m opening this issue to continue the discussion regarding random-vs-deterministic localIdentName class names in production builds.

⚠️ There were some important comments from @heavi5ide and @simonrelet which apparently were forgotten about, possibly due to a force-push which made it impossible to reach the original commit containing the comments (a15df83).


For ease of reading, I’m copying the relevant comments from here:

Click :point_left:

[@ro-savage] When using css modules, class names follow a deterministic convention rather than the standard random hash with the covention [directory]__[filename]___[classname].

Given src/components/Button/Button.module.css with a class .primary {}, the generated classname will be src-components-Button__Button-module___primary.

This is done to allow targeting off elements via classname, causes minimal overhead with gzip-enabled, and allows a developer to find component location in the devtools.


and here:

Click :point_left:

[@ro-savage] I’ve updated the pull request to use deterministic classnames rather than random hash, as suggested by another user (sorry can’t find the comment!).

Instead of someFileName__someClassName___hash it becomes directory__someFileName___someClassName.

This allows predictability as to what the class name will be, so that it can be targeted.

This can can not clash because the file would have to be in the same dir + same file name + same class name. Which if it is, it is the same class anyway. It also shouldn’t contribute very minimally to filesize because gzip should handle the repetition of long classnames.

This has been how react-scripts-cssmodules has been naming things for a couple months.


and here:

Click :point_left:

[@klzns] Should the production build expose the app architecture?

[@amannn] In regards to @klzns comment, I’d also appreciate not exposing the file path

[@andriijas] Why does it need to be deterministic and targetable? kind of kills the whole module thing. Why not recommend people to add additional static class if it needs to be targetable? Im upp for hashes.

[@sompylasar] Web marketing people often freak out if they cannot attach the out-of-the-box tools like HeapAnalytics or MouseFlow to specific elements of a website/webapp by id/classname (or if the classnames they attach to are changing with each release).


and here (you’ll need to click the “Show outdated” link to see the comments):

Click :point_left:

[@recidive] One thing that’s very useful, specially when you are using element based event tracking in your site/app, is naming classes based on component path instead of a hash so classnames don’t change on every build. This can be accomplished with localIdentName: '[path][name]__[local]'. Making classnames less volatile is useful for automations that rely on element classnames.

[@ro-savage] Is there any security implications of showing the repos paths?

[@ro-savage] After rewriting this to hash the path, I decided to see if I could to a PR to css-loader to add it as an option.

Once I looked at the source, I can see they are already generating the hash based on the path. options.content = options.hashPrefix + request + "+" + localName; which translates to relative/path/to/file+classname

I had never tested it before, but if you build it multiple times the classname should not change. Unless you change the file location or the classname itself.

@recidive & @heavi5ide are you getting different classnames with each build?

[@heavi5ide] Yes I just tested this out and it seems you are correct and this shouldn’t really be a problem at all. @recidive are you sure this is still an issue? It looks like the hash generation code that @ro-savage linked to in css-loader was changed “to something more reproducable” almost two years ago: webpack-contrib/css-loader@55ad466

[@simonrelet] I’m probably missing an important thing here and I understand the need for the naming during the development, but why isn’t it just [hash:base64:*] in production?


I think there are two key points which merit further discussion:


1️⃣ The concerns of @klzns and @amannn regarding exposing the source code’s file paths in production builds.

I share their concerns – I would feel uncomfortable seeing class names such as src-components-Button__Button-module___primary in production.  I understand the benefits of deterministic/predictable class names which don’t change with each build (so that elements can be targeted by class name), but this can be achieved without exposing the source code’s file paths.


2️⃣ The fact that css-loader’s [hash] token for localIdentName is actually deterministic (not random), as @heavi5ide and @simonrelet noticed.

Click for details of getLocalIdent() and interpolateName() :point_left:

CreateReactApp / ReactScripts currently uses css-loader v0.28.7.  Looking at the source code, the localIdentName template is passed to getLocalIdent(), which effectively generates the class name in this way:

//                Filepath of the CSS file, relative to the project root dir         Source CSS class name
options.content = path.relative(options.context, loaderContext.resourcePath) + "+" + localName;

loaderUtils.interpolateName(loaderContext, localIdentName, options);

interpolateName() is provided by Webpack’s loader-utils library, and the description of the [hash] token is:

the hash of options.content (Buffer) (by default it’s the hex digest of the md5 hash)

[<hashType>:hash:<digestType>:<length>] optionally one can configure

  • other hashTypes, i. e. sha1, md5, sha256, sha512
  • other digestTypes, i. e. hex, base26, base32, base36, base49, base52, base58, base62, base64
  • and length the length in chars

The default template for localIdentName is "[hash:base64]", which generates the MD5 hash (as raw binary data) and then base64-encodes it.


So [hash] is not random.  It’s the digest of: <Source CSS relative filepath> "+" <Source CSS class name>


So my recommendation is to change localIdentName in packages/react-scripts/config/webpack.config.prod.js from:

localIdentName: '[path]__[name]___[local]'

to something like:

localIdentName: '[hash:base64]'

The hash will be deterministic, predictable, unique and targetable, without exposing the source code’s file paths.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:6
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
andriijascommented, Feb 4, 2018

I’m 100% with you. The current localident is to verbose. And more importantly if you have third parties relying on your dom you should add other target points like data-target=“Foo” and not rely on how you style your components.

2reactions
sompylasarcommented, Feb 4, 2018

I’d like to emphasize that CSS styles being tied to HTML classes is a coincidence. Originally HTML classes were meant to describe the semantic meaning of an element in the markup regardless of the styling. CSS then targets these markup elements via one or more types of selectors (one being selector by HTML class) to add styles to them.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why Google use random ClassName. What is CSS Modules
CSS Module makes CSS modularized. In other words, CSS class names are automatically transformed to the unique CSS class names and make the...
Read more >
Understand the React styling paradigms - JavaScript Ramblings
There are 3 main styling paradigms in the React world right now, and the CSS in JS one can further be split into...
Read more >
A deep dive into CSS Module - LogRocket Blog
We can see that CSS Module dynamically generates unique class names, automating naming for our whole team. How CSS Module works. Modern tooling ......
Read more >
дэн on Twitter: "CSS Modules support just landed into Create ...
module.css When using css modules, class names follow a deterministic convention rather than the standard random h...
Read more >
CSS Classes considered harmful - Keith Cirkel
Class names are an archaic system that serves as a poor proxy for your UI ... we potentially produce a lot of duplicate...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found