Handling CSS variables (Custom Properties) in React

Published: · Updated: · Reading time: 10 min

In this article, I’ll explain how to handle (update) CSS variables in React. I’ll give you the concept for both Class components approach and Hooks approach.

CSS Custom Properties or variables are a great way to update styles in your app without using pre or post processors or creating additional CSS classes.

A quick reminder on the browser support for the CSS Variables:

Can I Use CSS Variables (Custom Proprties)? Data on support for the CSS Variables (Custom Properties) feature across the major browsers from caniuse.com.

In this example, I’ll be using a simple button component with an onCLick event which will change background-color and color properties of the button, just for the sake of demonstration.

Markup

For the markup, we’ll be using a button element inside a div container. The .container will store the CSS variables for the button.

<div class="container">
  <button className="btn">Click me!</button>
</div>

NOTE: The reason we’ve wrapped button inside a container is that there might be other elements and storing CSS variables on the container will scope them only for the child elements of this container.

Style

Let’s define the initial CSS variables on the .container element and use them on the button.

.container {
  --btn-bg-color: #ff9900;
  --btn-color: #1780cc;
}

.btn {
  background-color: var(--btn-bg-color);
  color: var(--btn-color);
  
  padding: 15px 30px;
  border: none;
  cursor: pointer;
}

Now for the React part. Conceptually Class and Hooks approaches are very similar, we’ll go through each one of them step by step.

1. React Class component approach

Let’s create a React class and name it ButtonClassComponent.

class ButtonClassComponent extends React.Component {

}

Then we’ll define the default state in the constructor method. The state will hold only one property isClicked.

constructor (props) {
  super(props)
  this.state = {
    isClicked: false
  }
}

Next, we’ll create a render method. As you can see, now it just returns the markup like above.

render () {
  let btnText = 'Click me!'

  return (
    <div className='container'>
      <button className='btn'>
        {btnText}
      </button>
    </div>
  )
}

Then let’s create a cssProperties object inside render method and assign this object to a style attribute. This way CSS variables and other CSS properties will be assigned to a .container element if exists.

render () {
  let cssProperties = {}
  let btnText = 'Click me!'

  return (
    <div className='container' style={cssProperties}>
      <button className='btn'>
        {btnText}
      </button>
    </div>
  )
}

Finally let’s add an onClick event to a button element. The handler will be a this.setState method call. And also let’s add a condition where we check if the button was clicked. If so, we’ll assign properties to a cssProperties object and change button text.

render () {
  let cssProperties = {}
  let btnText = 'Click me!'
  if (this.state.isClicked) {
    btnText = 'Hello World!'
    cssProperties['--btn-bg-color'] = '#1780cc'
    cssProperties['--btn-color'] = '#ff9900'
  }

  return (
    <div className='container' style={cssProperties}>
      <button className='btn' onClick={() => { this.setState({ isClicked: !this.state.isClicked }) }}>
        {btnText}
      </button>
    </div>
  )
}

Final React class component should look like this:

class ButtonClassComponent extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isClicked: false
    }
  }
  
  render () {
    let cssProperties = {}
    let btnText = 'Click me!'
    if (this.state.isClicked) {
      btnText = 'Hello World!'
      cssProperties['--btn-bg-color'] = '#1780cc'
      cssProperties['--btn-color'] = '#ff9900'
    }

    return (
      <div className='container' style={cssProperties}>
        <button className='btn' onClick={() => { this.setState({ isClicked: !this.state.isClicked }) }}>
          {btnText}
        </button>
      </div>
    )
  }
}

2. React Hooks approach

This is very similar to a Class component. For the Hooks approach, let’s create a function, and name it ButtonHooksComponent.

function ButtonHooksComponent () {

}

This function will return the known markup.

function ButtonHooksComponent () {
  let cssProperties = {}
  let btnText = 'Click me!'
  return (
    <div className='container' style={cssProperties}>
      <button className='btn'>
        {btnText}
      </button>
    </div>
  )
}

Assuming you’ve already imported useState hook, we’ll add a default state.

function ButtonHooksComponent () {
  const [ isClicked, setIsClicked ] = useState(false)

  let cssProperties = {}
  let btnText = 'Click me!'
  return (
    <div className='container' style={cssProperties}>
      <button className='btn'>
        {btnText}
      </button>
    </div>
  )
}

Finally let’s add an onClick event and a condition like in the class component.

function ButtonHooksComponent () {
  const [ isClicked, setIsClicked ] = useState(false)
  
  let cssProperties = {}
  let btnText = 'Click me!'
  if (isClicked) {
    btnText = 'Hello World!'
    cssProperties['--btn-bg-color'] = '#1780cc'
    cssProperties['--btn-color'] = '#ff9900'
  }
 
  return (
    <div className='container' style={cssProperties}>
      <button className='btn' onClick={() => {setIsClicked(!isClicked)}}>
        {btnText}
      </button>
    </div>
  )
}

That’s it!

So the main concept is to set the desired CSS properties to an object conditionally and then assign this object to a style attribute of an element.

You can use that approach not only for style attribute but also to set other HTML attributes as well. That way your code will be more readable and clear.

You can check the end result and whole code on CodePen:

See the Pen gOYdayy by Nikita Hlopov (@nikitahl) on CodePen.

Like this article? Share it on: