Better CSS-in-JS performance with zero runtime

Hello :wave:,

I was wondering if you could tell me, whether Frontity styled-components are zero runtime on parsing.

I came across this: https://dominictobias.medium.com/how-to-increase-css-in-js-performance-by-175x-f30ddeac6bce

So, should I go with https://github.com/callstack/linaria, Zero-runtime CSS in JS library with same styled-components syntax or stay with Frontity styled, or something else.

This could potentially save load times for my css heavy platform, let me know please thanks.

Hi @furrysmile2,

What do you mean by “zero runtime on parsing”?

Frontity is an opionionated framework to assure the final code generated by it is performant. Because of this it uses internally emotion as it’s tool that have proved to be flexible & performant


Frontity framework is very performance oriented as it takes care if generating a final bundle that is as lightest as possible among other things.

You can check the performance of some projects created with Frontity like the https://frontity.org/ or https://twentytwenty.frontity.org/

If you think Frontity could work better with an alternative CSS-In-JS library rather than emotion please, feel free to open a Feature Discussion

Many CSS-in-JS libraries parse the CSS string using a custom parser on the client. This increases the bundle size (albeit slightly) due to the inclusion of the parser. In addition, the CSS cannot be parsed until the JavaScript code is parsed and executed, which can be noticable, especially on low-end devices and bigger JS bundles.

Linaria is unique in the sense that it doesn’t need a runtime to work. Styles are parsed, evaluated and generated at build time and no extra parsing is needed on the client.

@mmczaplinski any thoughts on this?

I agree that using a “zero-runtime” CSS solution might result in better performance but I would not say that it’s a given, just because the that particular library is “zero-runtime”.

It might just be that in practice there is little difference in the user experience between using emotion and linaria because Frontity extracts the styles on the server during server-side-rendering.

So, the SSR with emotion might take a slightly longer time. However, on the client, there should be no difference in the user experience if you’re using SSR. It’s just that the “hydration” might take a little longer because the CSS from the styled components and css props are also parsed on the client, but the user is already looking at the SSR’ed page :slight_smile: .

That said, performance is a tricky subject full of caveats. Also I think that:

  1. Improving performance is not very important if it does not actually improve the UX.
  2. To talk about performance, you need to measure it first.

We could explore adding support for alternative CSS-in-JS libraries in Frontity for example via a package. @cristian.bote has suggested trying out https://github.com/cristianbote/goober which would also help us shave some bytes off the payload.

@furrysmile2 If you’d like to put together a proposal to add support for another CSS-in-JS framework or to replace emotion you’re welcome to open a Feature Discussion . I have not used linaria before, but we are always looking for ways to improve the framework! So, if there are convincing arguments in favour of using a different library (especially if it’s in ways that would be backwards-compatible) then we are definitely going to consider it :slight_smile:

1 Like

Yes I was just wondering if you knew about it before I test it myself, also in my case I am using Material UI style to style material components, and styled frontity to style custom components, would you recommend ditching Material UI style and using only frontity styled globally or?

I’m afraid I don’t know enough about your use case in order to make a recommendation on whether you should use material-ui or not.

I can only say that I know that Material-UI is a good framework with solid foundations. And in general component libraries are perfect for:

  • MVPs
  • SPAs that users are going to use heavily so that they won’t mind slightly larger bundles.
  • Projects that you don’t expect to evolve a lot over time.

That said can definitely use Material UI together with Frontity and it has great compatibility with emotion.

@furrysmile2 very interesting!! Thanks for sharing this approach and the article :slight_smile:


Just to add my two cents to what @mmczaplinski already said. There are some drawbacks with the zero-runtime approach for our use case:

  • We extract all the CSS from Emotion and put it in the HTML of the SSR’ed page. That means that CSS loads faster than using a .css file because it doesn’t need an additional server round trip.

  • Frontity support for Google AMP is coming. With Emotion, we can automatically move all the tree-shaken CSS from Emotion to the <head>. That means that Frontity Themes that only use Emotion will be 100% compatible with AMP out of the box.

  • With the linaria approach, there are restrictions on what you can do with dynamic styles at runtime in your CSS, which is one of the main benefits of CSS-in-JS over regular CSS.

    • It seems like they use CSS custom properties underneath, but they have worse compatibility with old browsers (that Frontity supports with SSR).
    • It seems like they can be used with styled but not with css. One workaround they propose is to use inline styles, but those are forbidden by Google AMP, so if Frontity themes would use them they wouldn’t be compatible with AMP out of the box. Another is again CSS custom properties, but those are not as nice as JS variables and have worse compatibility with older browsers.

As @mmczaplinski said, the performance cost is on the hydration phase, where Emotion executes again in the client. However, there is an upcoming solution that will help in that sense: the new React’s Server Components proposal. It is going to take a while until this is released and adopted, but it should make the hydration phase much lighter because the server components will skip the hydration phase. So this means that the only drawback of using the styled-components/emotion approach vs the linaria approach will mostly disappear eventually.


@cristian.bote any thoughts? You are the expert here on this topic :slight_smile:

To be honest, these guys have gone a long way extracting dynamic styles and converting those to plain CSS: https://github.com/callstack/linaria/blob/master/docs/BASICS.md. It’s quite impressive.

They also have an API to extract the critical CSS, so theoretically this should work with AMP: https://github.com/callstack/linaria/blob/master/docs/API.md#server-apis-linariaserver

Hey folks!

Thank you for opening this thread @furrysmile2, much appreciate it.

I believe @mmczaplinski and @luisherranz are summarising really well, what should look after when trying to choose how one should style a project.
Frontity is server side rendered, that means for each page, you’re gonna send only the css used + the global one, in the same http request trip. If you would use a compiled time styling library, you would either have to load the css file in it’s entirety, which is render blocking, or have to do the method that the linaria project is suggesting:

import { collect } from '@linaria/server';

const css = fs.readFileSync('./dist/styles.css', 'utf8');
const html = ReactDOMServer.renderToString(<App />);
const { critical, other } = collect(html, css);

// critical – returns critical CSS for given html
// other – returns the rest of styles

Source: https://github.com/callstack/linaria/blob/master/docs/API.md#collecthtml-string-css-string--string

This might be working fine, but if one looks closely, you would notice that on the server you are actually reading a css file on disk, and then scan the resulted html for the used classNames. This, in my opinion, it is really undesirable.

I wished the author of the article you’ve linked to, would have posted the source of the test or a link to the repo. There are a couple of red flags in the screenshots that I think needs to be checked before any claim. One thing that I do believe he is right about, is the use of css variables. Makes total sense and I do believe should be encouraged.

I wanna (re)assure you that the CSS-in-JS ecosystem is strong and has a solution to every performance bottleneck there is :slight_smile:. With that being said, emotion, is one of the top-notch libraries out there with really optimised runtime.

1 Like

Yes,

I am also using Material UI, and the CSS is not minified, on github it says to install postcss webpack plugin, now isn’t Frontity using these plugins to minify css?

Yes, Emotion minifies the CSS, even the one added using <Global>.

But it’s not minifying Material UI CSS

If you are adding CSS with styled, css or <Global> and it is not being minified when running Frontity in production mode, please open a bug in https://github.com/frontity/frontity and we will investigate :slightly_smiling_face:

No, it’s in material ui ssr,

 const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
  jssStyles.parentNode.removeChild(jssStyles);
}

exactly this: https://github.com/mui-org/material-ui/issues/23948

Hi @furrysmile2,

I don’t think the piece of code you’re sharing is executed in a Frontity project using Material UI

Frontity is a framework that simplifies the development of Isomorphics React apps connected to a Headless WordPress. As with any other React Framework you can use a CSS library to speed up the development of your Frontity project. In the case of Frontity the optimal option is choosing a CSS In JS library

Among other things, Frontity will take care of the SSR process that will return SEO friendly HTML which will be “hydrated” to add defined React behaviour to the HTML

That’s why, in a Frontity project the only one in charge of the SSR process is the framework itself