DEV Community

Aki Rautio
Aki Rautio

Posted on • Edited on • Originally published at akirautio.com

Creating a common component library with CSS and SASS in React.

There are more than enough options when deciding a toolset for building the common components in React. In this post, we are going to explore how to build common components with CSS and SASS and what kind of benefits and pitfalls there are.

From the all options, CSS files with SASS addition has the lowest learning curve because these tools are used very similarly to a normal web page development. CSS files are very simple to import to React component and SASS gives some nice optional additions like variables and calculations which ease up more complex work.

Setup

Setting up a CSS and SASS files to React project also very simple to do whether you are using Create React App or Webpack.

Optional: Create a separate component library package

In case you are doing a separate package, you need to take care of both the package and the consumer sides. On a separate package, you can either distribute the CSS files as is and let the consumer do the transpiling or transpile the CSS files already on the package and distribute the result. I would suggest using the first choice since it gives more options to the consumer.

Doing transpiling on the consumer side makes package creation easy. You need to ensure that both variable and styles files are included in the package when moved to the artifactory. The files need to be just copied to the build directory and then on the consumer side referred like Javascript files:

import '@packagename/styles.css';
import '@packagename/variables.css';
Enter fullscreen mode Exit fullscreen mode

If you want to do transpiling on the package side, you need to use your transpiler to create bundle style files. In case you are not using any bundler, I can suggest Rollup. Otherwise, find the needed plugin/extension. To transpile the sass files, you can use PostCss rollup plugin that includes the styles inside the javascript files or creates separate CSS bundle files.

See example from repository css-sass-rollup and sass-consumer.

Create React App

If you are using Create React App you just need to install node-sass.

npm install node-sass --save-dev

Webpack

With webpack, the project needs a bit more configuration but setup is still very straight forward. Besides node-sass we need to install sass-loader, css-loader and style-loader.

npm install node-sass sass-loader css-loader style-loader --save-dev

And then add all these to webpack.config.js

{
  test: /\.s[ac]ss$/i,
  use: ['style-loader', 'css-loader', 'sass-loader'],
}
Enter fullscreen mode Exit fullscreen mode

Parcel

Parcel handles both css and scss out of the box so you can start using them immediately without any configuration.

Importing css and scss files

Importing the scss / css files inside a component happens like any other import in javascript. For example:

import React from 'react'
import './style.scss'

const Box = () => (
   <div className="box">Box</div>
 )
export default Box
Enter fullscreen mode Exit fullscreen mode

Once CSS file is imported, the content becomes available everywhere in the application (as long as it is imported). This means you don't necessary have to import the css file where you are using the classes. In a sense this gives a huge flexibility regarding how to handle the styles. In a most extreme way you could import the css in a root JS file but I wouldn't suggest to do that.

How to structure the common components with css

Since CSS has a global namespace, it's very likely that at some point in a project two classnames overlap or otherwise cause issues. To mitigate these this, it's advisable to use some naming convention to separate the component from each other ensure that all unique components have their own classnames. One of the most popular is Block, Element,Modifier (BEM).

The idea with naming convention is the use same way to determine the classnames so that they are easy to recognize and reuse. Instead of making the classes be applicable for one use case like:

.disabled_btn {
 border: 1px solid $color-border;
 border-radius: $space-s;
  background-color: $color-disabled;
  color: $color-light-text;
Enter fullscreen mode Exit fullscreen mode

We would split the classes to be more generic and only add the necessary additions to more specific classes.

.btn {
  border: 1px solid $color-border;
  border-radius: $space-s;
  color: $color-dark-text;
}

.btn--disabled {
  background-color: $color-disabled;
  color: $color-light-text;
} 

Enter fullscreen mode Exit fullscreen mode

How to use classnames properly

This structure is very extendable and easy to use in different situations. We can use the css straight away:


const CoolButton = ({ children, classNames, onClick }) => (
  <button className={classNames} onClick={onClick}>{children}<button>
)
Enter fullscreen mode Exit fullscreen mode

or build a layer inside the component to handle classnames:

import classnames from 'classnames'

const Button = ({children, disabled, onClick}) => (
  <button 
    className={classnames('btn',{ 'btn--disabled': disabled })}
    onClick={onClick}
  >
   {children}
  <button>
)

Enter fullscreen mode Exit fullscreen mode

Note that in the latter example we are using classnames which helps out handle multiple class names tremendously.
While both ways of using class names are correct, I would strongly recommend using the second one where class names are handled inside the common component and only properties are exposed outside.

Having properties handle the class name changes limits the amount of available different ways the component can be manipulated, which simplifies the testing and ensuring that the changes won't break the design.

Unfortunately, there is no way to extend the common component when only exposing the properties so if you need an extendable component, it could be done by creating a base component with className property and build the used component without className property.

For example if we extend the previous example the used component would look like:

const Button = ({children, disabled, onClick}) => (
  <CoolButton 
    classNames={classnames('btn',{ 'btn--disabled': disabled })}
    onClick={onClick}
  >
   {children}
  <button>
)
Enter fullscreen mode Exit fullscreen mode

This way we get both extendability and limited amount of options.

Using variables from SCSS

Consistent design most often has a defined color palette and standardized spacing. With CSS and SCSS this can be done with variables added to be imported in the root javascript file (for example App.js)

The scss file could look like:


$background-Color: global #fff,
$primary1Color: global #408bbd,
$primary2Color: global #408bbd,
$secondary1Color: global none,
$secondary2Color: global #61b0e7,
...
$sSpacing: global '3px',
$mSpacing: global '6px',
$lSpacing: global '12px',
...

Enter fullscreen mode Exit fullscreen mode

And to be used other scss files like:


.btn--primary {
   background-color: $primary1Color;
   padding: $sSpacing;
}

Enter fullscreen mode Exit fullscreen mode

If the theme file starts to get bigger, there are also way to use SASS functions and mixins to help out keeping a better structure and easing the usages.

The benefit of using the global variables comes yet again from restrictions. When you limit yourself to use theme variables when defining colors or spacing, you also make sure that there is an easy overview what different option are used. This makes things yet again easier to test and ensure that everything works as they should.

Benefits and drawbacks of using CSS and SASS while creating the common components

As we have seen, CSS and SASS bring a powerful way to do common components/design without adding too much complexity. The component library would be easy to understand even by the developers who haven't done a lot with React and it most likely would be understandable for the people who mainly use only HTML + CSS.

The biggest advantage to use CSS and SASS is the convertibility. Since the styles are separated from the React component, the style can be reused between the frameworks. This gives a huge advantage if the same design is shared between an application that is not done only with React.

There are a couple of drawbacks too. Handling the class names manually creates a lot of possibilities to create an unmaintainable mess. The naming convention will help but this needs be managed constantly (or to have proper Eslint rules).

In my opinion, this toolset is still relevant and should be seen as an equal option when deciding what to use in a project.

Repository css-sass has an example how to use this in a project.

This is a second post from the series Speed up development by creating a common Component library. The later posts will cover up the other options to build the common component library and how to document the library.

Top comments (5)

Collapse
 
michaeloryl profile image
Michael Oryl

I don't see how this is creating a library of components. For example, if you use Create-React-App like you suggest, the 'yarn build' command makes a deployable bundle of the entire application, not something you could deploy to the NPM registry and pull in and use in another application (such as one itself built with CRA).

Am I missing something, or perhaps not taking "library" to mean the same thing that you are?

I've got existing applications that share copies of the same components. I want to package these common components up as something that each application could list as a dependency in package.json instead of doing a copy/paste. I'm using SCSS in the way you show here (importing), but I just don't see any means to actually make a library of components from what you have here.

Any help would be appreciated. Thanks.

Collapse
 
akirautio profile image
Aki Rautio

Great question! :) You are absolutely right that building the package with yarn build will result an application instead of package. The focus in here is more to show power of different tools when developing the common components. Creating a package would be probably a good topic for an article but here couple of main points:

The package is just one directory inside node_modules which can be imported from the application the way you please. This means that in a simplest form you don't have to do any builds because that's a job for the application which consumes the package. Usually some preparation is still done so that imports don't look like following and correct packages are also installed when consuming the package.

import Button from 'library/src/components/Button/Button'

Now this is where the personal preferences comes to play since there are a lot of tools and ways to achieve the same results. If you are looking any tools to build cjs, esm or umd files for a package I would suggest to try rollup.js. Note that SASS won't be needed to build at this point because that's the job for the application who consumes the common library.

For configuring the package.json file, I would suggest to look this Creating your first npm package .

Hopefully these helped :)

Collapse
 
soorajnairhs profile image
soorajnairhs • Edited

Hi Aki,
Thanks for all this information. I've created a component library like you mentioned. I've tried and use create-react-library to package. I've a theme.scss file which takes in other files like colors.scss, dimensions.scss, fonts.scss etc. These files have respective scss variables lile $primaryColor, $primaryPadding etc.
When I publish the package, I'm able to import controls in consumer app, like
import Button from 'component-repo';
However, I'm only able to see a compiled version of scss files, as just one css file.
I would like these variables to be updated by consuming application. The package I mentioned earlier uses rollup. Is there a way not to compile scss variables, and keep it as it is, and thereby making the component library actually useful. Any sort of help is well appreciated.

Thread Thread
 
soorajnairhs profile image
soorajnairhs

With the help of Aki, I was able to solve this issue. I've tried to put together the content in a stack overflow answer, over here.
stackoverflow.com/a/66670203/11027191
Many thanks for your support Aki :-)

Thread Thread
 
akirautio profile image
Aki Rautio

Thanks :) And superb that you wrote about it since I'm pretty sure that you are not the only having the same struggle.