DEV Community

Cover image for Faster React apps coding: How to migrate from Emotion CSS-in-JS to Stylify Utility-First CSS
Vladimír Macháček
Vladimír Macháček

Posted on • Originally published at stylifycss.com

Faster React apps coding: How to migrate from Emotion CSS-in-JS to Stylify Utility-First CSS

Looking for a better way to manage your CSS in React? Learn how to migrate from Emotion CSS-in-JS to Stylify CSS's utility-first approach and streamline your development workflow.

You can try the examples in this article in the Playground On Stackblitz 🚀.

💎 Introduction

Stylify is a library that uses CSS-like selectors to generate optimized utility-first CSS based on what you write.

Features:
✅ Build module. No runtime script.
✅ CSS-like selectors
✅ No framework to study
✅ Less time spent in docs
✅ Mangled & Extremely small CSS
✅ No CSS purge needed
✅ Components, Variables, Custom selectors
✅ It can generate multiple CSS bundles

🔗 Components

In Emotion, components are often defined this way:

const Title = styled.h1`
  color: blue 
  font-weight: bold
  @media (max-width: 640px) {
    color:red
  }
`;
<Title>Hello World!🎉</Title>
Enter fullscreen mode Exit fullscreen mode

Stylify provides a similar feature. Components can be defined within a file (using content options), where they are used, or globally within a config.

Example with the configuration within a file. The content between stylify-components expects javascript object without surrounding brackets:

<!-- 
stylify-components
  title: 'color:blue font-weight:bold md:color:red'
/stylify-components
-->
<h1 class="title"></h1>
Enter fullscreen mode Exit fullscreen mode

Example in a global compiler config:

const compilerConfig = {
  title: 'color:blue font-weight:bold md:color:red'
};
Enter fullscreen mode Exit fullscreen mode

Usage:

<h1 class="title"></h1>
Enter fullscreen mode Exit fullscreen mode

Components are "lazy" (generated on demand). This means, that even if you configure them (in a file or globally), they will be generated only if matched within the content. No unused CSS will be generated. The same goes for utilities. If the utility for a component is not matched within a content directly, the selector is not generated and only the component selector is added to the CSS output.

The production CSS output will look something like this in production:

.a,.d {color:blue}
.b,.d {font-weight:bold}
 @media (max-width: 768px) {
 .c,.d {color:red}
 }
Enter fullscreen mode Exit fullscreen mode

The html output:

<h1 class="d"></h1>
Enter fullscreen mode Exit fullscreen mode

🎯 Selectors

In Emotion, you can use CSS prop to style element directly like this:

<div css={css`
  color: blue;
  font-weight: bold;
  @media (min-width: 640px) {
    color: red;
  }
`}></div>
Enter fullscreen mode Exit fullscreen mode

Stylify allows you to use utilities directly within the content. So the above example can be refactored to this:

<div className="color:blue font-weight:bold md:color:red"></div>
Enter fullscreen mode Exit fullscreen mode

The production output of the CSS will be similar to the example of the components. The HTML however will look like this:

<div className="a b c"></div>
Enter fullscreen mode Exit fullscreen mode

🌐 Global Styles

Sometimes we need to style some parts of the application globally. To do that within the Emotion, we need to use the Global component:

<Global
  styles={css`
    .some-class {
      color: hotpink !important;
    }
  `}
/>
Enter fullscreen mode Exit fullscreen mode

In the case of Stylify, you can use custom selectors to solve this problem. These selectors can be defined directly within the class attribute or in the global config.

Example with the class attribute:

<div className="[.button_.icon]{font-size:14px}">
  <button className="
    [.icon]{color:#fff;border-radius:12px}
    [&+button]{margin-left:24px}
  ">
    <i className="icon"></i>
  </button>
  <button></button>
<div>
Enter fullscreen mode Exit fullscreen mode

The syntax pattern in the class attribute looks like this:

[css selectors]{stylify selectors split by ;}
Enter fullscreen mode Exit fullscreen mode

The _ (underscore) is used instead of space in both CSS and Stylify selectors and the & character always refers to the current element.

The same code but in the global config would look like this:

const compilerConfig = {
  customSelectors: {
    '.buttons-wrapper .button .icon': 'font-size:14px',
    '.button': `
       .icon { color:#fff border-radius:12px }
       & + button { margin-left:24px }
    `,
  }
}
Enter fullscreen mode Exit fullscreen mode

When defining customSelectors in the global config, the syntax lets you use a nesting feature. The & characters refer to the upper level.

Usage of the global config:

<div className="buttons-wrapper">
  <button className="button">
    <i className="icon"></i>
  </button>
  <button></button>
<div>
Enter fullscreen mode Exit fullscreen mode

💲Variables

When you need to pass a color into the component using props, then instead of doing this color: ${props => props.textColor};, you can use native CSS variables:

<div 
  style={{ '--localTextColor': props.textColor }}
  className="title color:$localTextColor"
">
</div>
Enter fullscreen mode Exit fullscreen mode

We just need to tell Stylify to replace variables with CSS variables instead of their value and that the localTextColor is external:

const compilerConfig = {
  replaceVariablesByCssVariables: true,
  externalVariables: ['localTextColor']
}
Enter fullscreen mode Exit fullscreen mode

The external variable can also be defined only in the file where it is used:

<!-- 
stylify-externalVariables
  localTextColor
/stylify-externalVariables
-->
<div 
  style={{ '--localTextColor': props.textColor }}
  className="title color:$localTextColor"
">
Enter fullscreen mode Exit fullscreen mode

Stylify also provides an option to configure custom variables. It can be done in the file where they are used, in the same way as components or in the global config:

In file:

<!-- 
stylify-variables
  primary: '#000',
  secondary: '#444'
/stylify-variables
-->
<div class="color:primary"></div>
Enter fullscreen mode Exit fullscreen mode

In Compiler Config:

const compilerConfig = {
  primary: '#000'
}
Enter fullscreen mode Exit fullscreen mode

📦 Splitting CSS

Emotion splits CSS automatically and injects it directly into the document based on the rendered components.

Stylify doesn't have any runtime script, so you have to configure the Bundler and the splitting manually.

However, the Stylify output is so small (it can be even 10 Kb (gzipped) for a large website), that it is ok to have only one bundle for the whole project. Eventually, there you can check tips for CSS bundles splitting.

Bundles config example:

const bundles = [
  { 
     outputFile: 'path/to/layout.css', 
     files: ['path/to/layout.jsx'] 
  },

  // Bundler uses https://npmjs.com/package/fast-glob
  // You can use its glob syntax
  { 
     outputFile: 'path/to/global.css', 
     files: ['path/**/*.jsx'] 
  }
]);
Enter fullscreen mode Exit fullscreen mode

Let me know what you think!

If you like the idea, let me know by starring Stylify repo ❤️.

I will be happy for any feedback! The Stylify is still a new Library and there is a lot of space for improvement 🙂.

Top comments (10)

Collapse
 
joelbonetr profile image
JoelBonetR 🥇

Why anyone would do that? 😅

If you're in need for SEO means that probably you are using some tool that provides SSR, like Next JS, which SSRs styled-components or emotion so the issue is nonexistent...

Collapse
 
machy8 profile image
Vladimír Macháček • Edited

@joelbonetr
How does SEO relates to this article? This article is about migrating from Emotion to Stylify. No matter the use case => Next.js, Vite+React, others...

Collapse
 
joelbonetr profile image
JoelBonetR 🥇 • Edited

My bad for reading fast. I kept the concept fast into my mind and jumped into performance, thought the reasons for performance concerns and SEO was the Occam's razor on this 😅

Leaving this aside, I'm trying to see it from my point of view (Tech Lead) and the mental path goes this way:

Imagine you're a tech lead of a company (or product, or product line) with an already existing product in production which uses emotion or styled-components, and you suddenly want to migrate to Stylify (or Tailwind or whatever), but you need to justify the cost of doing so to the guys who manage the money.

How do you justify the cost?

You'll need to answer some questions:

  • How many human resources will be working on that?
  • For how many time?
  • Which benefits will outcome from this migration?
  • Which are the risks? (maturity of the tech, community support, how much -time and effort, thus monies- costs to train people on this? How many people out there already knows this tech in case we need to hire some more people?)

The other way around

Let's keep imaging you're the same tech lead working close with the software architects trying to define a tech stack for a new project.

  • Would you choose Stylify over any other option?
  • Justify your choice.

Everything that's behind money needs to be justified somehow (unless you're the government lol) and some of us need to scratch our heads trying to find the best fit answering those questions above and many more.

What are we?

Think of it as we are consumers of other software products. I'm currently a consumer of Next JS, styled-components, Sequelize, Mongoose, TypeScript, React and many more, but in my case, as I work mostly in projects backed by money that's not mine, I need to make choices objectively because that's what it's expected from me.

Can I be a consumer of Stylify? It will depend on what does it offer and at a which price/cost.

Final thoughts

I don't want to be mean, I understand that's a project you did and that you want to promote it, kudos on that! 😁 I just happened to be the bad cop in this conversation 😅

I'd suggest, if you want to push this Stylify thingy forward, that you do it objectively. code React Apps faster is something subjective that you may achieve by already knowing Stylify A LOT, not something realistic for us, the readers.

Also, the post begins with

Looking for a better way to manage your CSS in React?

And after reading it completely I still don't know why Stylify would be better than Emotion, and that bugs me 🙃

Best regards

Thread Thread
 
machy8 profile image
Vladimír Macháček

I still have no idea how CSS, generated by either Stylify or Emotion has any impact on SEO. Yes, slow pages have a lower ranking, but I can't imagine how large the CSS would have to be or how many render-blocking resources you would have to load so it has any impact on SEO, because the size of the CSS generated by these tools is small and in the case of Stylify it can be easily under 10KB for the whole web.

I understand you may be looking at this article from the position of Tech Lead, but this article is not focused on that at all. It's about a simple and quick syntax/feature comparison, so just by looking on the code of each tool you can see the possible alternative solution that might be better than the other.

The plus and cons of migration from one tool to another is for a long debate. It depends on the project, time and its budget as you said.
But it is always possible to migrate and therefore the issue you marked as nonexistent, actually exists. Otherwise, projects once written in jQuery would still be written in jQuery and yet, devs and companies are migrating these projects to pure javascript or something else without creating a whole new project. I would actually say, that in web development, you always migrate from something to something (for example during BC breaks). So this is nothing new and it's called an upgrade. But I understand that somebody may rather stay with tools he is used to.

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇

Agree, this is totally an issue of expectations.

I expected from a post that explains how to migrate from one tool to another to explain why should I do that in the first place as well (benefits, in which situations should I consider it and so on), so I could consider looking forward into it and maybe proposing it for some project (wink, wink).

PS: that wasn't a rant against it but an intent of constructive criticism that you may want to use to promote Stylify on a better way. Few individuals will try it out without a reason that justifies the time and effort of doing so.

If you write a post that covers those questions people may want to check it out and if anyone decides to migrate will probably check out the docs (I'd suggest adding a migration point in the docs with side-by-side code comparison and workarounds depending on the tool).

By answering those questions you may convince some companies to use your product as well, in which case it may be a boost for it!

Thread Thread
 
machy8 profile image
Vladimír Macháček

I am taking no offense. I just had no idea, why you talk about SEO...

Anyway, yes, these articles are not filled with information as much as I would want them to be, unfortunately, I have no time to write long stories.

The migration guide with features comparison is planned, but I haven't uploaded it yet.

Btw thanks for the response/feedback!

Thread Thread
 
joelbonetr profile image
JoelBonetR 🥇

Anytime!

I feel interesting that you've decided to build a build module with this concept.
If you happen to publish a detailed post around all this stuff ping me somehow! I want to know more 😁

Best regards

Thread Thread
 
machy8 profile image
Vladimír Macháček

What kind of "detailed" you mean? What information would you be interested in?

There is a blog
And in the docs there are sections like Compiler, Bundler and Unplugin that explains a lot of what is going under the hood.

Collapse
 
ohadbaehr profile image
OhadBaehr

I don't understand why not just use css at this point... also, 28kb is more then most css in js libraries out there

Collapse
 
machy8 profile image
Vladimír Macháček • Edited

@ohadbaehr Stylify is a build module. It's mentioned as a first point in the list at the beginning of the article Build module. No runtime script. You do not include it in the production application. It generates CSS during webpack/vite/rollup build and that's all. So only the generated CSS size counts, which can be 6 KB gzipped for the whole web.