Search code examples
reactjssassrolluprollupjs

How to write A React component library that allow scss variables to be overwriten


I have few react projects which have few component they share. So I wanted to create a library that contains the components they share.

For example I have a ActionButton component which looks like this:

import React from 'react';
import PropTypes from 'prop-types';
import './ActionButton.scss';

const ActionButton = ({ children }) => (
    <button className="action-button"> {children}</button>
 )

export default ActionButton;

This is the scss file:

@import "../../styles/variables";

.action-button {
    background-color: $primary; // this is defined in the variables scss file.
}

and in variable.scss

$primary: blue !default;

Now I want to be able to create a library with that ActionButton with the ability to change the value of $primary.

Im using rollup to build the library with this configuration:

import babel from 'rollup-plugin-babel'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import postcss from 'rollup-plugin-postcss'
import sass from 'rollup-plugin-sass';
import scssVariable from 'rollup-plugin-sass-variables'
import resolve from 'rollup-plugin-node-resolve'
import url from 'rollup-plugin-url'
import svgr from '@svgr/rollup'
import pkg from './package.json'

export default {
    input: 'src/index.js',
    treeshake: false,
    output: [
    {
       file: pkg.main,
       format: 'cjs',
       sourcemap: true
    },
    {
      file: pkg.module,
      format: 'es',
      sourcemap: true
    }
  ],
   plugins: [
     external(),
     postcss({
       modules: false,
       extract: true
     }),
     url(),
     svgr(),
     babel({
        exclude: 'node_modules/**',
        plugins: [ 'external-helpers' ]
     }),
     resolve({
       extensions: ['.js', '.jsx'],
     }),
     commonjs(),
     scssVariable(),
   ]
}

Now in the project that uses this library I tried to import the variables.scss and overwrite the variable but it does not work:

// this is in the package that uses this library
@import '<PackageName>/src/styles/variables.scss;
$primary: red;

Then when I draw the ActionButton it is still blue.

What do I need to do in my library to allow this kind of functionality?


Solution

  • Just import !default variables into your particular project before the component's variables.

    This is the description from SASS docs:

    Variable Defaults: !default You can assign to variables if they aren't already assigned by adding the !default flag to the end of the value. This means that if the variable has already been assigned to, it won't be re-assigned, but if it doesn't have a value yet, it will be given one.

    It converts to logic, then if the default variable value was already defined in the project it would not be redefined by the next default value. Only first !default variable value applies.

    Example:

    $primary: blueviolet !default;
    $primary: greenyellow !default;
    
    .element {
      color: $primary; // will resolve to 'blueviolet'
    }
    

    Check on stackblitz: https://stackblitz.com/edit/react-sfbwtj?file=style.scss

    UPDATE: To use it with your current config, please import styles directly with scss files from your library:

    // this is in the package that uses this library
    @import './your-redefined-variables.scss';
    @import '<PackageName>/src/styles/variables.scss';
    @import '<PackageName>/src/styles/library-styles.scss';
    
    .element {
      color: $primary;
    }