CSS in JS is a relatively new technique that lets developers write CSS code directly inside the Javascript code instead of having separate CSS files.

The key benefits are:

  1. Dead code/rules and tree shaking: By using Javascript tools you can easily detect unused CSS rules and remove them from the bundle.
  2. CSS isolation: CSS class names are dynamically generated; they are typically smaller and more isolated than regular CSS.
  3. Download size: Small CSS names and the embedding of CSS in the application bundle leads to fewer network requests.
  4. Javascript flexibility: Even though several tools (SASS, Less, etc) exist to write CSS more efficiently, nothing can beat the power of a full programming language!
  5. Code sharing: All code is in the same location and therefore you can easily share constants like colours and sizes between CSS and application code.
  6. Static analysis: Use of standard Javascript tools makes it possible to ensure no invalid CSS rules or values are used.

The usage of CSS in JS is obviously not the solution to all performances problems and there are many developers who heavily criticize the concept behind it, for example, posts such as this. Several solutions also exist that enable important performances to benefit by just using plain CSS code, for example, CSS modules. As always, as developers, we just have to choose the tool and technique that best fits the product requirements and team preferences.

In this article, we demonstrate how to embrace CSS in JS in a React application.

Choosing a CSS in JS tool

There are several open source CSS in JS tools available. Looking at a non-exhaustive sample list like this there are more than twenty options.

All the tools provide more or less the same functionality and most of the time are framework-agnostic, so the choice is really up to you.

In our case, we want to choose tools that are easy to introduce in the stack without too much complexity. So all tools that use the Higher Order Component (HOC) approach are excluded.

HOC uses the idea that a developer passes a component to a function which adds new functionality. The result is then used in place of the original component.

Similarly, one of the benefits we wanted was static type analysis, therefore tools that used ES 6 template literals to write the CSS are excluded as well. Only tools that accepted CSS as Javascript objects are considered for inclusion.

Amongst all existing CSS in JS tools, we chose TypeStyle for the following reasons:

  1. It uses plain Javascript to write code instead of enforcing usage of Javascript templates and similar.
  2. It has complete TypeScript typings. For editors that support it, like Visual Studio Code, it enables complete static analysis and unused code detection at development time.
  3. It is framework-agnostic and can be embedded easily into most frameworks without fundamentally changing the way you write your application.
  4. It has utilities and related packages for media queries, colours and animation.
  5. Since rules are appended to a
     

    Generic Rules and Advanced Media Queries

    TypeStyle also lets you define rules with custom selectors. These rules are applied immediately to the document and no class names are generated. Let's say we want to show our spinner in the centre of the page. We take advantage of the CSS Flexbox module and this task is trivial.
    typestyle.cssRule('html, body', {
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      minHeight: percent(100),
      fontSize: '10pt' // This sets 1rem = 10px
    })

    Going back to media queries, there is something to consider when working with them. While CSS is applied top-bottom, TypeStyle rules are not. Collision-free classes names usually ensure this is not a problem, but media queries targeting the same class may show some unexpected behaviour.

    Take this stylesheet example:

    div {
      color: black;
    }
    
    @media (max-width: 600px) {
      div {
        color: red;
      }
    }
    
    @media (max-width: 400px) {
      div {
        color: yellow;
      }
    }

    When applying this to a document the media queries are executed top-bottom, therefore when the window size is below 400 pixels the div text colour is yellow.

    When using TypeStyle, you typically translate the stylesheet above to the following definition:

    const divClassName = style(
      {
        color: 'black'
      },
      media({maxWidth: 600}, {
        color: 'red'
      }),
      media({maxWidth: 400}, {
        color: 'yellow'
      })
    )

    TypeStyle doesn't guarantee the order of generation of the classes (especially since this is dependent on the JS engine), so we need to adapt the media queries not to generate ambiguity.

    The code above can be fixed this way:

    const divClassName = style(
      {
        color: 'black'
      },
      media({maxWidth: 600, minWidth: 401}, {
        color: 'red'
      }),
      media({maxWidth: 400}, {
        color: 'yellow'
      })
    )

    Server-Side Rendering and Critical CSS

    TypeStyle makes Server-Side Rendering (SSR) very easy. The core idea is that once you get your server-generated HTML ready in some variable, you make a call to the getStyle function to flush all the used styles so far.

    This also has the benefit of ensuring that only used CSS rules are sent in the response - this is the so-called critical CSS.

    Here is a simple example of how to generate a full HTML page using React and TypeStyle on the server:

    app.get('/', function (req, res) {
      const body = ReactDOMServer.renderToStaticMarkup()
    
      const html = ReactDOMServer.renderToStaticMarkup(
            
    
    

     

    
          
        
      )
    
      res.send(html);
    });

    Drawbacks and Improvements on the age Performances

    Embedding TypeStyle in the code, of course, has its drawbacks. In particular, the addition of the 2 new libraries increased the PWA application code size by roughly 15KB.

    The good news is that while this additional data is loading, the application and the user interaction is not blocked at all. The application has already been rendered on the server and all the CSS required has already landed on the device with the HTML page; so the browser has everything it needs to render the page.

    Also, regarding the increase - when in fast network environments such additional data is hardly perceivable, on slow or unreliable networks it is balanced by huge benefits on the first-render time.

    This was the original performance of page load on the original branch using just plain CSS.

    CSS in JS type checking

    By using TypeStyle, we've been able to almost reduce the first render time on a slow 3G network by 50%.

    CSS in JS type checking

    To give an overview on how TypeStyle helps in limited situations we have run the same comparison on a different mobile network condition using the profile settings used by webpagetest:

    NameDownload Speed (Kbps)Upload Speed (Kbps)Latency (ms)
    EDGE240240840
    2G280256800
    Slow 3G400768400
    Fast 3G1600768170
    4G90009000150
    LTE120001200070

    As you can see from the graphs below the benefits are more perceivable in the slower networks where each network request is really expensive. Even in most performant network; embedding using the CSS in JS technique reduces the first render time by 100ms.

    CSS in JS type checking

    In all cases, CSS in JS increases the first interactive at most by 100ms. As outlined before, the decrease of first render time makes it acceptable if we look at the increase in users conversion rates.

    CSS in JS type checking

    Conclusion

    The introduction of PWAs has radically changed the way we develop our applications. Mobile devices are now capable of storing information offline in ways that were not even imaginable a few years ago.

    PWAs introduce various new ways of serving content in unforgiving network environments. We must not forget that there needs to be a network to load the cache in the first place. Every application is different and the introduction of service workers allow developers to provide custom caching strategies which can better fit the specific use case.

    In this post we have outlined how using CSS in JS can help in reducing the application size and the amount of network requests required to first load it. While this technique is not a silver bullet for all modern Web application performance problems, it leads to a significant reduction in the first render time.

Don’t miss a beat

Get all the latest NearForm news, from technology to design.
Sign Up
View all posts  |  Technology  |  Business  |  Culture  |  Opinion  |  Design
Follow us for more information on this and other topics.
Published by Paolo Insogna
15th June 2018