Dynamically load a stylesheet with React Dynamically load a stylesheet with React reactjs reactjs

Dynamically load a stylesheet with React


Just update stylesheet's path that you want to be dynamically loaded by using react's state.

import * as React from 'react';export default class MainPage extends React.Component{    constructor(props){        super(props);        this.state = {stylePath: 'style1.css'};    }    handleButtonClick(){        this.setState({stylePath: 'style2.css'});    }    render(){        return (            <div>                <link rel="stylesheet" type="text/css" href={this.state.stylePath} />                <button type="button" onClick={this.handleButtonClick.bind(this)}>Click to update stylesheet</button>            </div>        )    }};

Also, I have implemented it as react component. You can install via npm install react-dynamic-style-loader.
Check my github repository to examine:
https://github.com/burakhanalkan/react-dynamic-style-loader


This is prime mixin teritority. First we'll define a helper to manage style sheets.

We need a function that loads a style sheet, and returns a promise for its success. Style sheets are actually pretty insane to detect load on...

function loadStyleSheet(url){  var sheet = document.createElement('link');  sheet.rel = 'stylesheet';  sheet.href = url;  sheet.type = 'text/css';  document.head.appendChild(sheet);  var _timer;  // TODO: handle failure  return new Promise(function(resolve){    sheet.onload = resolve;    sheet.addEventListener('load', resolve);    sheet.onreadystatechange = function(){      if (sheet.readyState === 'loaded' || sheet.readyState === 'complete') {        resolve();      }    };    _timer = setInterval(function(){      try {        for (var i=0; i<document.styleSheets.length; i++) {          if (document.styleSheets[i].href === sheet.href) resolve();        } catch(e) { /* the stylesheet wasn't loaded */ }      }    }, 250);  })  .then(function(){ clearInterval(_timer); return link; });}

Well $#!@... I was expecting to just stick an onload on it, but nope. This is untested, so please update it if there are any bugs – it's compiled from several blog articles.

The rest is fairly straight forward:

  • allow loading a stylesheet
  • update state when it's available (to prevent FOUC)
  • unload any loaded stylesheets when the component unmounts
  • handle all the async goodness
var mixin = {  componentWillMount: function(){    this._stylesheetPromises = [];  },  loadStyleSheet: function(name, url){    this._stylesheetPromises.push(loadStyleSheet(url))    .then(function(link){      var update = {};      update[name] = true;      this.setState(update);    }.bind(this));  },  componentWillUnmount: function(){    this._stylesheetPromises.forEach(function(p){      // we use the promises because unmount before the download finishes is possible      p.then(function(link){        // guard against it being otherwise removed        if (link.parentNode) link.parentNode.removeChild(link);      });    });  }};

Again, untested, please update this if there are any issues.

Now we have the component.

React.createClass({  getInitialState: function(){    return {foo: false};  },  componentDidMount: function(){    this.loadStyleSheet('foo', '/css/views/foo.css');  },  render: function(){    if (!this.state.foo) {      return <div />    }    // return conent that depends on styles  }});

The only remaining todo is checking if the style sheet already exists before trying to load it. Hopefully this at least gets you on the right path.


I think that Burakhan answer is correct but it is weird to load <Link href = "" /> inside the body tag. That's why I think it should be modified to the following [ I use React hooks]:

import * as React from 'react';export default MainPage = (props) => {  const [ stylePath, setStylePath ] = useState("style1.css");      const handleButtonClick = () => {    setStylePath({stylePath: 'style2.css'});  }  useEffect(() => {    var head = document.head;    var link = document.createElement("link");    link.type = "text/css";    link.rel = "stylesheet";    link.href = stylePath;    head.appendChild(link);    return () => { head.removeChild(link); }  }, [stylePath]);  return (    <div>      <button type="button" onClick={handleButtonClick}>        Click to update stylesheet      </button>    </div>  );};