Understanding Emotion Styled Components by implementing the library from scratch

christianschuller
intive Developers
Published in
6 min readSep 27, 2022

The Emotion Styled components library makes styling React components easy, but have you ever wondered how they actually work and why you can use this weird backtick syntax? 🤯

In this tutorial we are going to explore what is happening behind the scenes when we create styled components with Emotion by implementing a small version of the library ourselves.

In the end we will have a styled.div function that can be used like the original implementations.

Have a look at a working code example here: link

Working example of our styled components implementation

So without further ado…let’s learn something new! 🥳

What is happening when we create styled components?

When we create a styled component using…

…a few things happen:

  • A CSS string is created from the value within the backticks
  • A unique class name is generated for this CSS string
  • The class and the corresponding CSS is injected into a style tag inside the head element of the DOM
  • The div element is added to the virtual DOM using React with the unique class name assigned to its className attribute

Like this the created element has a className that refers to a class in the head section of the website. This class contains the CSS that we defined in backticks, which leads to the element being styled in the way that we wanted.

Let’s discover how we can implement this styled.div-function step by step!

1 — Getting the CSS string from within the backticks

For this we need to understand tag functions. Tag functions are syntactic sugar in Javascript that let you pass the parameters of a function in the form of a template string. They look like this:

When you pass a template string to a function, all of the pure string parts of the template string are passed in the form of an array as the first function parameter. The variable parts of the template string are passed each one as the remaining parameters of the function. So an equivalent function to the above tag function greet would be:

Thank you to Brandon Morelli for the good explanation in his article here.

2 — Applying our knowledge about tag functions to styled.div

Let’s apply our knowledge about tag functions to styled.div:

When we call…

…we are actually calling:

“So what about access to props and the theme?” , you might ask.

If we provide a theme to Emotion using a ThemeProvider component, we can access it along with the component’s props within the template string like this:

But how is this working?

As we can see, we are passing a list of strings and two functions as parameters here. The equivalent function call in regular JS syntax would look like this:

In order to access the parameters of the tag function and to test if our assumptions were right, let’s create the styled.div function. Note that we are aggregating the functions into an array using the …-operator:

Output:

Output of our styled.div function — two arrays of strings

Perfect! We have access to the list of strings and we can also call the two functions, which are able to access the props and the theme within the props.

But we have one problem: Now we have two arrays of strings that we have to combine to create the full CSS string:

"height: 200px; background: blue;"

Let’s create a function that merges our two string arrays!

We can now run our mergeCssStrings function inside of our styled.div function and log the merged CSS string:

Output:

Output of mergeCssString returns the CSS string with values from the props and theme

3 — Create a unique class name for the CSS string

Now that we have created a CSS string referring to values from props and theme, we need to create a unique class name for it before injecting the class into the DOM. For that we are using a simple id generator called nanoid.

After installing nanoid and importing it we can use it like this:

Note that CSS class names cannot start with a number. That is why we add the div-prefix before the id.

4 — Injecting the class into the DOM

After generating the CSS string and className, we need to inject the class into the head-element of the DOM. For this we will create a function called injectCssIntoDom. Let us have a look at the implementation:

Our function injectCssIntoDom takes a className and a cssString as parameters. It searches for a style tag with the id “myemotion-styled”. If it doesn’t already exist, it creates one in the head element. After that it combines className and cssString into a class and appends it to the style tag. We can then call this function in styled.div.

5 — Create a React component with the corresponding className, pass props and access theme

We have come a huge way! In the next step we need to make sure that our styled.div-function returns a React component and that this component can then be instantiated with props like a normal function component. It also needs access to the theme in a themeContext. With that in place, we don’t need to provide props and theme in the same JS file like we did in the code further up. The following code snippet should demonstrate how we would like to be able to use our styled.div-function:

For that we have to adapt the styled.div-function:

We are almost there! Our styled.div-function now returns a function that takes in props. These props are then merged with the theme using a useTheme-hook. A CSS string and class name is generated and we inject the class into the DOM. Finally, we return a React element, pass it the className as prop and pass the down the children.

The useTheme-hook looks like this:

6 — Creating a styled-function for all HTML-tags

In the last step we have to make the styled function work with different types of HTML-tags. To achieve this, we simply add a function for all supported DOM-elements to our styled-object.

This is the final implemenation of styled:

7 — Usage of our own styled-function

Import the styled object and use it like the original @emotion/styled function.

Output:

Hero component created by our own implementation of Emotion styled — 200px large and blue background

8 — Next steps

We are now injecting a CSS class with a specific class name into the DOM whenever one of our styled components is rendered. Rendering many times, this will pollute the head-tag of the Website with unused classes.

In a next step, we should work on preventing this from happening by creating class names that are somehow dependent on the component that is rendered, maybe using a hash of the component. Whenever the component is unmounted, the class styling this specific component should be removed from the DOM.

Additionally we could work on passing valid HTML attributes directly down to the HTML-Element when we call the React.createElement-function. Currently we are only passing a className as prop which is added to the respective Element. But for that we would need to extract only valid HTML-attributes from the props to pass them down to the Element. Otherwise React will complain that it doesn’t know what to do with the custom attributes inside of the HTML tag. Emotion does this in the @emotion/is-prop-valid package and it can be replicated quite easily.

But these would be topics for a follow-up article…

9— Thank you! ❤️

Thank you for reading through this tutorial about understanding how Emotion styled components work (on a high level) by implementing the library from scratch.

I hope you liked it! In case you have any comments or doubts, please feel free to write them in the comment. If you liked the tutorial, I would be happy to have you as a new follower or to receive a coffee from you!

I hope to see you soon! Stay curious! 🤓😃

--

--