emotion icon indicating copy to clipboard operation
emotion copied to clipboard

Investigating Significant Performance Discrepancy Between @emotion/react and @emotion/css

Open mbondyra opened this issue 11 months ago • 4 comments

Hi Emotion team 👋

First off, thank you for your amazing work on Emotion. We’re long-time users at Elastic and really appreciate the flexibility and power the library offers.

We’re currently working on a large-scale migration from SCSS and styled-components to Emotion, and during performance investigations, we’ve uncovered significant and repeatable performance differences between @emotion/react and @emotion/css. The tests were run in Kibana using reproducible test cases, and later simplified into a CodeSandbox.

Summary of Findings

  1. @emotion/react renders significantly slower than @emotion/css.

  2. Memoizing styles improves performance with @emotion/css but has negligible (or sometimes negative) impact when using @emotion/react.

  3. Some optimizations (like moving conditionals outside of css) only benefit @emotion/css.

We suspect the root issue might be the lack of Emotion Babel plugin optimizations (e.g., caching), since Kibana currently includes both styled-components and Emotion, which may prevent us from using Emotion’s Babel plugin fully. Plus we cannot use babel plugin in codesandbox.

Questions

  1. Is this performance difference expected between @emotion/react and @emotion/css?

  2. Does the lack of the Babel plugin severely penalize @emotion/react performance? (even if using object syntax like we do)

  3. Are there known scenarios where @emotion/css should be preferred for performance-critical paths?

  4. Can @emotion/react be optimized in any way without dropping support for styled-components (which is still in our codebase)?

  5. Would you be open to profiling some of these examples or confirming if this is something Emotion itself could improve?

Repro Info The idea of the app I wrote is simple – we show multiple scenarios on the page and render X components Y times. There's a radio list at the top of the page at the right, to choose between scenarios that render the same components using @emotion/css and @emotion/react.

CodeSandbox

The test case renders 2,000 components 3x across several strategies (inline, memoized, externalized, etc.), but the trends are visible with way less.

Here are some performance stats (at the bottom of the page) for @emotion/css:

Image

And for @emotion/react:

Image

Happy to provide more benchmarks, screenshots, or collaborate on further tests if it helps. This is quite impactful for us as we define best practices for Emotion usage at scale, and any help or clarity would be incredibly appreciated.

Thanks again!

Other examples:

Image

mbondyra avatar May 22 '25 13:05 mbondyra

Thank you for a detailed issue. It is on my radar and I hope to investigate and respond in the coming week.

Andarist avatar May 24 '25 09:05 Andarist

Thanks @Andarist! While I have your attention, I'd have one more question when you find a moment — slightly off-topic, but I'm curious what you think about the performance concerns raised in this article: https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b

I’ve done some pretty extensive testing on components with heavy use of css props (and compared to SCSS), and I haven’t been able to reproduce anything close to the kind of slowdown the author reports. In my tests, Emotion doesn't seem to cause significant runtime overhead — in real-life scenarios it’s more like a 1%-5% or less difference (so not really significant), not the 50%+ reported there.

I’m just wondering how to explain such a big difference. Do you think it's outdated benchmarks, maybe a very specific setup, or something else? Curious to hear your take!

mbondyra avatar May 26 '25 13:05 mbondyra

As to the article above... I'm not exactly sure - I have not been able to reproduce such overhead in the benchmarks I've done in the past. The more crazy you go with dynamic patterns the runtime cost naturally increases but I, honestly, always have seen the CSS-in-JS cost to be negligible.

As to the reported problems, I think the main issue here is the bigger React element tree that @emotion/react creates to get some boundaries and guarantees in place. It's not exactly something we can change right now without introducing breaking changes. I'm working on Emotion 12 (no ETA) and it's definitely something I'll try to address there (I already had a plan to address at least some of this). As to no ETA for this - there is some work that has to be done in React itself that is blocking further work on this in Emotion. I'm in contact with the React team, and we'll be discussing our options soon.

I'd happily discuss further over a call what your next steps could be and how could I assist you, if that's something you'd be up for.

Andarist avatar May 28 '25 08:05 Andarist

Thanks a lot for the detailed reply, @Andarist — really appreciate you taking the time to dig into this and share some context. I’d definitely be open to a call – I’ll follow up via DM to coordinate.

mbondyra avatar May 28 '25 12:05 mbondyra