loading...
Cover image for What is the best way to distribute styles with npm package?

What is the best way to distribute styles with npm package?

stereobooster profile image stereobooster ・2 min read

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

Posted on by:

stereobooster profile

stereobooster

@stereobooster

Hello, I'm a full stack web developer. Follow me on Twitter!

Discussion

pic
Editor guide
 

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.

  • first your styles might be become bound to the users environment, like CSSModules for .css
  • next you might affect the users environment, like shipping SC4 while user has SC2

So - I would say - CSS. CSS with a webpack-loader of you choice, or node-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 a node-loader. Like - you may process source CSS and transform to the thing you need, like you may do using webpack-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 :)

 

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.