The Vanilla Extract library is not like any other CSS-in-JS solution. It’s sweet, it has sprinkles and it’s definitely good for you! Today, I’ll show you how to introduce those zero-runtime stylesheets in TypeScript straight into your software project.
The battle of frontend world domination between frameworks seems to slow down and new solutions appear less frequently than a few years ago. It doesn’t mean that we’ve found excellent solutions for everything. There are still several issues in creating web frontend apps that are a field for experimentation.
One of my personal favourites is application styling.
At first, I was excited about LESS and SCSS, then I fell in love with the component approach in CSS Modules and later switched to Styled Components and various variations on CSS in JS. Recently, people have been persuading to abandon the BEM methodology in favour of Tailwind.
Don’t you just feel dizzy from all those frequent course changes?
Now, we are heading towards componentizing, move away from CSS syntax, appreciate the advantages of the atomic approach. But what’s next? Well… I can see something on the horizon that may hit the sweet spot.
Meet Vanilla Extract! 🧁
👇 NEW CSS-IN-JS LIBRARY ALERT!
🔥 Zero-runtime Stylesheets-in-TypeScript
✨ Minimal abstraction over standard CSS
🦄 Works with any front-end framework
🌳 Locally scoped classes + CSS Variables
🎨 High-level theming systemhttps://t.co/V4P4BYRDrE pic.twitter.com/rbw8IlomQi
— 🧁🍨 Mark Dalgleish (@markdalgleish) March 26, 2021
Vanilla Extract library benefits
Vanilla extract library is an interesting solution for several reasons:
This means that the JS code used to style the components never ends up in the browser. Only generated, ready-to-run CSSs do this. The action of converting JS into CSS takes place on the developer’s side, you save the user’s browser much effort and gain a bit of loading speed on your pages. At the same time, you also keep all the advantages of CSS-in-JS solutions.
As in CSS Modules, Vanilla Extract also uses safe, hashed class names. In traditional CSS, you have to make sure that class names don’t repeat, otherwise, you risk mixing styles in the application. I’ll give you an example: the CSS attributes of the user’s photo and the commenter’s avatar can merge if you use for both images classes named user-photo.
Like many other modern libraries, Vanilla Extract solves this problem by generating stylesheets where all classes have a unique name. Later in the code you can just import those classes into your components and you will get variables storing those generated class names. Those are simple strings that you can apply to the HTML of your application.
Scoped CSS variables
What you won’t see in CSS Modules, but available in Vanilla Extract, are scoped CSS variables. The variables you create will be available only for the styles you describe and the styles they descend from. It may not sound fascinating, but it is really powerful, as I will show in the examples later. Of course, these variables get hashed as well, so just like with classes, there is no risk of name collisions.
Other benefits of Vanilla Extract
Vanilla Extract benefits include also…
- creating themes where you can store the configuration of colours, fonts and spacing for the rest of your styles,
- full support for TypeScript with excellent code completion which makes styling the application simple and fun, and also…
- …typechecking that saves from setting an incorrect value for the CSS attribute or accidentally deleting a variable (thanks to this, you will avoid a long investigation, for example why a given element is not centred).
Okay, enough theory, let’s check out what it can actually do. It’s time to prepare your project for the Vanilla Extract library.
Vanilla Extract with Create React App
The Vanilla Extract documentation describes several ways to install it in your project depending on the main framework used. At the time of writing this article, however, there is nothing on how to connect it with the popular Create React App starter, so I decided to demonstrate how to do it.
Vanilla Extract requires a special approach because, as described in the previous section, it is a zero-runtime solution, i.e. the entire JS to CSS transformation takes place while building your app. For this reason, you have to modify the process of code-building, and in the case of CRA, this is not doable out of the box.
Take it easy, lil’ hamster. You don’t have to eject the entire Creat React App configuration (as it is done when you want to heavily interfere with the Webpack and Babel settings there). You can use the Craco library. It will allow you to easily add all the things we need to build Vanilla Extract stylesheets in the most popular starter for React.
I assume you already have a project configured with Create React App, so you can go straight to installing the necessary Vanilla Extract libraries.
If you don’t have a CSS file extraction plugin in your project yet, you need to add it to the dependency list as well.
Then add the previously mentioned Craco to the project, i.e. a tool that will allow you to overwrite the CRA configuration.
Now you need to slightly modify the commands in package.json so that the build process is run with Craco.
With that behind you, it’s time to reconfigure the build process. To do this, we need to create a craco.config.js file at the root of the project and put this code there:
As you can see, we have added plugins for Babel and Webpack so the code of your styles is properly interpreted and the generated files are attached to your bundle.
And that’s all! Now your project is all set to style with Vanilla Extract.
Styling basics with Vanilla Extract library
As I’ve mentioned before, you need to keep the styles you write in separate files with the extension .css.ts. Webpack will recognize these files and compile them to CSS sheets, and only the resulting class names will be exported from the files you create. Let’s see an example:
You define styles using objects with CSS attributes. The names of these attributes must be written using camelCase. So instead of
background-color we have
z-index changes to
Your class is now ready, so it’s time to import it into the application component:
And here’s the effect of your work:
As you can see from the classes’ names generated by Vanilla Extract, you can easily tell what component this class comes from and what its original name was.
Of course, when writing styles, you can also use advanced selectors. Here’s an example straight from the library documentation that shows how to use pseudoselectors, child selectors, and at-rules like media and support.
When defining classes, you can also refer to other classes created with the
There is one requirement imposed by Vanilla Export – that such extensive selectors target only the elements with assigned classes created with this library. Selector should end with an ampersant to target a class it is defined in (e.g.
.content &:hover) or another class created with Vanilla Extract (e.g.
This is not a technical limitation of the library itself, but a good practice that it imposes to make our classes easier to maintain.
However, if you would like to style many elements in advance, or simply add
styles to the body, you can use a special function that will define global styles in your application.
Unfortunately, Vanilla Extract doesn’t support nested global styles as of writing. Therefore, idle and hover links must be styled with two calls to the
Themes and variables
You’re probably thinking… If it has nothing to do with your grandma’s cake recipe, where does the Vanilla Extract library take its name?
Vanilla Extract is just a thin layer of logic superimposed on the native CSS mechanisms. Unlike other libraries, it does not try to reinvent the wheel and implement cascading style mechanisms in its way. Instead, it makes heavy use of the CSS variable functionality built into browsers.
Therefore, the themes in Vanilla Extract are not, as is usually the case in similar solutions, ordinary configuration objects. Here, when you define a theme, you are creating a collection of CSS variables, one variable for each parameter of that theme. When you use them, you don’t assign those parameters values to the CSS attribute, but the names of the CSS variables that hold the value.
Fear not, lil’ Obi-Wan. It will be much easier to understand this in practice.
Creating a theme
First, you create a theme. In this case, it will only define the main color of our application.
createTheme function returns an array with two values. The first is just a string with the class name that defines all the CSS variables resulting from your theme configuration. Plug this class in a selected place in the application, where the theme you create is to apply.
With browsers devtools, you can easily preview its content. In this example, it’s just one CSS variable, with the main application color you set before:
Using the theme
Now go back to the second variable that you got when creating the theme. Inside var is an object with the same shape as the one you passed to the
createTheme function. But! All object field values have been replaced with references to the CSS variables that hold these values.
You can now easily use these variables to create new classes. Yay!
Now, when you use such a class in a component, the border color will be set in it using the CSS variable, not by direct value substitution.
This approach is very different from other popular libraries or preprocessors. They create their own system of variables or shared configurations that have no direct reference in the resulting styles.
At the same time, what Vanilla Extract proposes is a more convenient solution than using pure CSS variables directly. It provides convenient code suggestions and also prevents the use of undefined variables. Thanks to this, it is easy to refactor styles without fear that after removing unnecessary settings from the theme, something somewhere in the app will stop working because these parameters were needed there.
More advanced stuff
In addition, Vanilla Extract allows performing more advanced operations. For example, you can create child themes that modify some of the variables of the original configuration. Continuing with the previous example, you can now add a separate theme for your dashboard:
When you display an item with a previously created box class inside this theme, it will have a red border.
Note that the two themes are actually independent of each other, and in HTML you don’t need to nest one inside the other! Passing vars as the first
createTheme parameter is only to ensure that both themes operate on a configuration with the same shape. Because of that for example adding a new field to the child theme will fail with a compilation error.
Vanilla Extract also allows, just like in pure CSS, to overwrite CSS variables for specific classes. In this library, this is done by adding the
vars field to the class description and setting your variable there to a different new value:
Interestingly, creating CSS variables in Vanilla Extract is not limited to themes. You can easily create your own single variables and use them to define classes. The following code snippet demonstrates this perfectly:
My chapters on styling and theming obviously don’t cover everything that can be done in Vanilla Extract. They only show the general concept behind this library. For more details, see the official Vanilla Extract documentation. There you will find information on how to use keyframes and font-faces and how to make responsive themes.
Dynamic styling. A little more action, please!
Vanilla Extract also provides several dynamic styling functions that can be useful if you want the CSS values to depend on the state of our component. Instead of setting theme parameters in the files compiled during the build, they will be assigned on the fly in the mounted component of your application. This will allow you to easily change the appearance of page elements depending on the user’s action or data coming from the server.
To use this option, you must first add another dependency:
Now you need to define your theme. In the example, I will define a full-fledged theme, but sometimes it is enough to specify the shape of the theme parameters. This thing is called a theme contract. You will find more information on this, as always, in the README file.
Back in the example, the theme looks like this:
You will also need the styling class of the widget that you will display in the component. When defining this class, refer to the variables from the previously created theme:
And that’s all you need to do on the .css.ts file side.
With that ready, you can move on to writing the component body. Start by adding the simplest state and a button that will change that state:
Inside this component, you now need to add a local theme based on the static theme you created a moment earlier. Most importantly, this theme will modify some of its values based on the current state inside the component.
Depending on whether the flag is enabled or not, the background in the theme will be set to red or green. When you are ready, all you need to do is to impose the local theme and the class you wrote in the app component:
Congrats, your dynamic styles are ready! Now pressing the button will change the state value, and this will affect the background color of the widget. You can find the entire code for this component here:
And this is how it works:
Vanilla Extract library – summary
I showed you some possibilities offered by the Vanilla Extract library. Nowadays, when every CSS-in-JS preprocessor or library tries to implement its themes and variables mechanism, such a return to the native capabilities of the browser seems encouraging.
And although Vanilla Extract library is not as recognizable as its competition yet, I think it will quickly gain popularity thanks to the idea that the creators had for it: static CSS sheets and native CSS variables.
I think the Vanilla Extract library will soon convince many application developers who are tired of applying another thick layer of abstraction on something that should be simple and pleasant…
Finally, it is also worth mentioning that Vanilla Extract is only part of a larger system. If you are interested, I invite you to read about the Sprinkles library (cute, right? 🍨) based on Vanilla Extract and is intended to easily create your own equivalent of Tailwind CSS.
Now you probably got cravings for muffins, so go and support your local bakery!