Setting CSS variables from a React app

In my React application, I needed a way to pass custom variables from my JavaScript code to my CSS code. For example, I want to do this:

function DemoApp() {
  return <MyCustomButton marginTop={"10px"} />;
}

Then, MyCustomButton will need to set its margin top to 10px. So, I need a mechanism to take the props values and pass it down to CSS and set it. Let's see how it's down. I am using CSS Modules in my React app, btw.

/** my-custom-button.js **/
import styles from "./my-custom-button.module.css";
function MyCustomButton() {
  return <button className={styles.button}></button>;
}

Defining CSS variables

To use dynamic values in CSS, one approach is through CSS Variables. I can define a CSS variable as --my-var: 10px and then access it with margin-top: var(--my-var);. So for mt MyCustomButton component, I defined the required CSS variables first.

/* my-custom-button.module.css */
.button {
  /* these variables will be used later to set from JavaScript */
  --marginTop: 0;
  --marginBottom: 0;

  margin-top: var(--marginTop);
  margin-bottom: var(--marginBottom);
}

Now that I have my CSS variables defined, next step is to set them.

Setting CSS variables from React component

I can pass style object to JSX that react will apply to the underlying element. I need to construct this style object with all the variables that I want to set.

/** my-custom-button.js **/
import styles from "./my-custom-button.module.css";
function MyCustomButton(props) {
  return (
    <button
      className={styles.button}
      style={{
        "--marginTop": props.marginTop,
        "--marginBottom": props.marginBottom,
      }}
    ></button>
  );
}

This way, I can pass in whatever the custom props to the underlying CSS variable.

Using the utility method to construct the style object

Manually constructing the style object will not scale well if you have 10s of custom attributes. In such cases, we can write a simple function that construct the style object for us. We can also add any validation to the code.

/** my-custom-button.js **/
import styles from "./my-custom-button.module.css";

/**
 *
 * @param props react props object with key, value pairs
 * @param variableNames array of names, for ex. ['marginTop', 'marginBottom']
 * @returns a object such as {"--marginTop": "10px", "--marginBottom": "10px"}
 */
function propsToCSSVariable(props, variableNames) {
  return variableNames.reduce((acc, key) => {
    if (props[key] !== undefined) {
      acc[`--${key}`] = props[key];
    }
    return acc;
  }, {});
}

const cssVariableNames = ["marginTop", "marginBottom"];
function MyCustomButton(props) {
  return (
    <button
      className={styles.button}
      style={propsToCSSVariable(props, cssVariableNames)}
    ></button>
  );
}

With this approach, now I can completely customize my React components with JavaScript and CSS.

If you are following any other techniques, please do let me know.