Let's say we have an npm package with React components. These components have/need styling. Some styles are overridable, for example, colors or dimension (aka theming). Some styles are not overridable, for example, some component-specific positioning, imagine styles that are needed to position cells in calendar component.
CSS files
We can do it old-school way by providing CSS files. If the consumer uses webpack or Parcel they can do something like this:
import { Component } from "package"
import "package/styles.css";
If there is no bundler developer would need to put styles in the header
<link href="package/styles.css" rel="stylesheet">
How to do theming? We can try to use CSS variables. But the downside is that there is no safety in that process. What if instead of --primarycolor: red;
developer writes --primary: red;
, there is no type system no runtime check that will prevent the error.
As well stylesheets expose implementation details. Stylesheet becomes part of the public API. If we need to change the internal markup of the component, it can affect the structure of CSS, and if it does we will need to bump the major version of the library (because it can be breaking change for somebody).
We will need to support unique specifiers (class names or similar) for our components. We can try to take Reach UI approach and use data params instead of classes, for example [data-reach-menu-item]
. But again, there is no safety in this approach, what if developer mistyped specifier or specifier changed with the major library update.
CSS-in-JS
We can use emotion or JSS to style components. Question of theming is solved. No issue with unique classifiers. There is safety in place - we can use typescript or add runtime checks.
But it will have runtime, which can be an issue (from performance POV). It will increase the size of the component (we can use peerDependencies
, but then the package is not consumable without bundler). What if the developer uses more than one component and each comes with its own CSS-in-JS solution 😱? We will need to do the additional configuration for SSR.
Other
We can use style
property as well, but we need to write some code to do theming (maybe with Context).
We can use className
prop and assume that consumer will use CSS Modules. Again, we need to think about how to pass it to deeply nested components (maybe with Context?).
Use CSS-in-JS solution, which could be compiled away with a zero-runtime solution like linaria or astroturf, so the developer who doesn't want runtime can remove it, but still have all DX benefits. I haven't seen this in practice, this is just my dream.
What would you recommend?
As you can see there are trade-offs in each approach. What would you recommend? Did you try to solve a similar task? Share your experience.
Photo by Erik Eastman on Unsplash
Top comments (7)
It depends on SSR. Without SSR it it could be a simple
inject-style-tag
solution, like react-style-singleton.With SSR it become complex.
.css
So - I would say - CSS. CSS with a
webpack-loader
of you choice, ornode-loader
to do the same, like css-modules-require-hook. It's just keeps a bit more freedom for everyone.react-style-singleton is interesting I need to play with it. I didn't quite get the idea behind css-modules-require-hook, what the value there
css-modules-require-hook
is just a good example of anode-loader
. Like - you may process source CSS and transform to the thing you need, like you may do usingwebpack-loader
.Almost nobody consider this as an option, or calling some loader-based approaches bad, cos they are not repeatable for node, while they are :)
What about babel-plugin-transform-postcss? I think this is better than css-modules-require-hook, because postcss and postcss-modules are used directly. And no CSS is actually included in the resulting JavaScript
A very good option
I was recently stumbled upon this situation and finally uploaded the CSS file from package itself.
I also generate one asset manifest file which has location of main css file.
Now, it's upto consumer app to use it as
<Link>
tag or add it in their bundle.Some responses from twitter: