Esteban Herrera Family man, Java and JavaScript developer. Swift and VR/AR hobbyist. Like books, movies, and still trying many things. eherrera.net

Using the StyleSheetManager component in styled-components 5.0

3 min read 1040

Styled-components version 5 brings some interesting enhancements to the StyleSheetManager component, a helper component that you can use for modifying the processing of your CSS styles.

In addition to the sheet property, used to provide a StyleSheet object with custom configuration, and the target property, used to provide the DOM node where the styles will be injected, the StyleSheetManager component now has the following properties:

  • disableCSSOMInjection
  • disableVendorPrefixes
  • stylisPlugins

Let’s review them in detail.

Modifying the way CSS is injected

The property disableCSSOMInjections allows us to switch from the CSS Object Model (CSSOM) API to a text node-based CSS injection system.

When a browser parses the HTML code of a page, in addition to creating a tree of nodes called the DOM (Document Object Model), it creates a CSS object model in the form of a tree where each node contains CSS style information for a particular DOM element.

This way, to insert or modify the style of a particular node, we can use either the DOM API:

document.getElementById('myDiv').style.background = 'blue'

Or the CSSOM API:

// Assuming there's a stylesheet in the HTML page
const style = document.styleSheets[0];
style.sheet.insertRule('#myDiv {background-color: blue}');

This way, when the property disableCSSOMInjections is present or you assign it the value true:

ReactDOM.render(
   <StyleSheetManager disableCSSOMInjections={true}>
      <App />
   </StyleSheetManager>,
   root
);

Styled-components will use a text node-based CSS system for getting, inserting and deleting styles:

export class TextTag implements Tag {
  element: HTMLStyleElement;
  nodes: NodeList<Node>;
  length: number;

  // ...

  insertRule(index: number, rule: string): boolean {
    // ...
    this.element.insertBefore(node, refNode || null);
    // ...
  }

  deleteRule(index: number): void {
    this.element.removeChild(this.nodes[index]);
    // ...
  }

  getRule(index: number): string {
    // ...
    return this.nodes[index].textContent;
  }
}

Instead of the CSSOM API:

We made a custom demo for .
No really. Click here to check it out.

export class CSSOMTag implements Tag {
  element: HTMLStyleElement;
  sheet: CSSStyleSheet;
  length: number;

  // ...

  insertRule(index: number, rule: string): boolean {
    // ..
    this.sheet.insertRule(rule, index);
    // ...
  }

  deleteRule(index: number): void {
    this.sheet.deleteRule(index);
    // ...
  }

  getRule(index: number): string {
    const rule = this.sheet.cssRules[index];
    // ..
    return rule.cssText;
  }
}

Here you can see the entire source code of these components to see how it’s done.

However, probably you’ll only need to use this property when having integration issues with third-party tools since the CSSOM API performs better and is the recommended option.

Disabling CSS browser prefixes

As the name implies, the property disableVendorPrefixes disables the generation of CSS vendor prefixes (also known as CSS browser prefixes):

  • Chrome/Safari: -webkit-
  • Firefox: -moz-
  • Internet Explorer/Edge: -ms-
  • Opera: -o-

For example, the rule transform: rotate(15deg) is generated with vendor prefixes by default:

But if the property disableVendorPrefixes is present or you assign it the value true, the vendor prefixes won’t be generated:

the vendor prefixes aren't generated

Try it here.

Use this property only if you don’t need legacy browser support.

Customizing the way CSS is processed

At a high level, this is what styled-components does when you declare a style:

  1. It evaluates the tagged template
  2. It generates the CSS class name
  3. It preprocesses the CSS rules with a library called Stylis (actually a fork, @emotion/stylis)
  4. It injects the preprocessed CSS into the page

What’s new in version 5 is the ability to use plugins to customize the way Stylis preprocesses the CSS rules with the property stylisPlugins.

The most popular plugin at this time seems to be stylis-plugin-rtl, which adds RTL (right-to-left) support to the app (notice that this plugin is a fork of the original Stylis RTL plugin).

Basically, RTL changes the values of the CSS properties that deal with horizontal values, such as box and text shadows, border, left, and right positions, among others.

To use this plugin, we just have to import it and pass it to the stylisPlugins property, which accepts an array of plugins. Here’s an example using the radius of a border:

import rtlPlugin from "stylis-plugin-rtl";

const Title = styled.h1`
  border: solid 5px;
  border-radius: 35px 0px 0 35px; /* top-left top-right bottom-right bottom-left */
  color: red;
`;

// ...

<StyleSheetManager
  stylisPlugins={ [rtlPlugin]}
>
   <div className="App">
      <Title>Hello world!</Title>
   </div>
</StyleSheetManager>    

Without the plugin:
code without the plugin

With the plugin:

with plugin

Here you can try an example of this RTL plugin and another plugin that adds support for the mso- vendor prefix.

You can look for more stylis plugins on NPM. However, at the time of this writing, not all of them are completely compatible with styled-components.

For example, some plugins will throw the following error:

A stylis plugin has been supplied that is not named. We need a name for each plugin to be able to prevent styling collisions between different stylis configurations within the same app. Before you pass your plugin to <StyleSheetManager stylisPlugins={[]}>, please make sure each plugin is uniquely-named.

If this is the case, you’ll only need to add a name property to the plugin instance, like this:

import msoPlugin from "stylis-plugin-mso";
Object.defineProperty(msoPlugin, "name", { value: "msoPlugin" });

On the other hand, some plugins work by returning a string that represents the processed CSS rule. The original rule will be replaced with this string.

But other plugins don’t return a value, they process the CSS rules using the references passed as arguments.

Here’s the signature of a plugin function, where the most important parameters are context, the current stage of the processing, and content, the CSS rule that is being processed:

function stylisPlugin(context, content, selectors, parent, line, column, length) {
  // ...
}

In most cases, these types of plugins (that don’t return a value) are not compatible with styled-components.

If you want to build a custom plugin, you can learn more here.

Conclusion

Released in January 2020, styled-components v5 extends the ability to customize the way your CSS styles are processed without any breaking changes to its API.

You have learned about the three new properties of the StyleSheetManager component:

  •  disableCSSOMInjection— to switch to a text node-based CSS injection system for adding styles to the DOM instead of using the CSS Object Model (CSSOM) API
  • disableVendorPrefixes— to disable the generation of browser prefixes for some CSS properties
  • stylisPlugins— to process your CSS rules with Stylis plugins

Probably the last one is going to be the most popular property, just keep in mind the caveats mentioned above when using Stylis plugins.

Happy coding!

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Is your frontend hogging your users' CPU?

    As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

    LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

    Modernize how you debug web apps — .

    Esteban Herrera Family man, Java and JavaScript developer. Swift and VR/AR hobbyist. Like books, movies, and still trying many things. eherrera.net

    Leave a Reply