DEV Community

Discussion on: Why Would Anyone Use Constructible Stylesheets, Anyways?

Collapse
westbrook profile image
Westbrook Johnson Author

Hey look, someone finally did the research and found the 5-8x performance improvement that was hiding in this article...who knew? 😜

Potential 5x perf improvement for stylesheet updates by switching to `CSSStyleSheet.replaceSync` #2501

The problem

Currently, hot module reloading pages that use Emotion with React is about 8x slower than using CSSStyleSheet.replaceSync to update styles.

Here are two Chrome profiles where the same CSS is updated on disk 1024 times, sleeping for 32ms between each update. In the first case, it's a <Global> React component, and in the second case, it's a <link> tag being hot reloaded

with-emotion-react.json.gz - this one is using Emotion and the React Component is being re-imported each time. Note the difference in time spent on Rendering between the two profiles.

CleanShot 2021-10-07 at 23 35 22@2x

with-replaceSync.json.gz - this one is using CSSStyleSheet.replaceSync

CleanShot 2021-10-07 at 23 35 11@2x

This is the CSS:

:root {
  --timestamp: "16336741341477";
  --interval: "32";
  --progress-bar: 56.889%;
  --spinner-1-muted: rgb(179, 6, 202);
  --spinner-1-primary: rgb(224, 8, 253);
  --spinner-2-muted: rgb(22, 188, 124);
  --spinner-2-primary: rgb(27, 235, 155);
  --spinner-3-muted: rgb(89, 72, 0);
  --spinner-3-primary: rgb(111, 90, 0);
  --spinner-4-muted: rgb(18, 84, 202);
  --spinner-4-primary: rgb(23, 105, 253);
  --spinner-rotate: 304deg;
}
Enter fullscreen mode Exit fullscreen mode

This is the React component using Emotion:

import { Global } from "@emotion/react";
export function CSSInJSStyles() {
  return (
    <Global
      styles={`
:root {
  --timestamp: "16336721342556";
  --interval: "32";
  --progress-bar: 56.889%;
  --spinner-1-muted: rgb(179, 6, 202);
  --spinner-1-primary: rgb(224, 8, 253);
  --spinner-2-muted: rgb(22, 188, 124);
  --spinner-2-primary: rgb(27, 235, 155);
  --spinner-3-muted: rgb(89, 72, 0);
  --spinner-3-primary: rgb(111, 90, 0);
  --spinner-4-muted: rgb(18, 84, 202);
  --spinner-4-primary: rgb(23, 105, 253);
  --spinner-rotate: 304deg;
}  `}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Proposed solution

Detect if CSSStyleSheet.replaceSync is supported in the current browser and use that to update the existing stylesheet (rather than creating a new one). This would work for both development and production (in production for dynamic styles).

Drawbacks:

  • This API is only available in Chromium browsers. Multiple "backends" for updating styles introduces complexity
  • @import is not supported with CSSStyleSheet.replaceSync

Alternative solutions

Additional context

replaceSync has some cost, but it's not so bad:

CleanShot 2021-10-07 at 23 58 24@2x

Versus:

CleanShot 2021-10-07 at 23 58 53@2x

Incase opening the profiles are a pain, here are screenshots.

Emotion: CleanShot 2021-10-08 at 00 09 54@2x

replaceSync: CleanShot 2021-10-08 at 00 08 16@2x