Intro & Background
If you have some experience with React, you probably came across styled-components. In the last few years, the concept of css-in-js became more popular, and there are multiple libraries that are available for us to use. styled-components is one of them, but you can also find Emotion, Radium, JSS, and more. In this post I'm not going to cover the pros and cons of traditional stylesheet files vs. styled-components, and instead - I'm going to focus on tagged template literals - the "magic" that let us use the styled-components
syntax.
styled-component basics - a quick reminder
Let's take the following simple syntax for example:
The
StyledDiv
in the example above is actually a React component that returns a div
block with the css of color: red; font-weight: bold;
.Well... kind of. Actually - it's a bit more complicated than that. The output of the above is a div with specific css class-names having the above css definitions inside:
Some of you are probably using this without giving it too much of a thought. If we take a closer look we can see the usage of the backtick (`
) right after the styled.div
.
This syntax in Javascript is called Tagged Template Literals.
Template Literals
Let's start with Template Literals
, and then move on to the more interesting concept.
In javascript - template literals are strings that can contain expressions within them:
As you can see from the example above - expressions can be variables, but are not limited to them:
We can use either variable or function inside a template literal, and the value that will be used is basically the string representation of the expression:
Now that we understand the power of template literals - it's time to move on to tagged template literals - or just tagged templates.
Tagged Templates - Whats the fuzz?
With tagged templates, we have the power to parse the template literal ourselves using our own "home-made" function.
Note that in the example above - the variable
strB
contains the string String A
(and not String B
as you might expect).
Let's break it down
- The function
funcA
returns the stringString A
. - By using the function
funcA
as a tagged template - we completely ignored the string that was sent, and we just return something else. - We can use it with an empty string, and the output will be the same.
Check it out:
Advanced features
The function we use in a tagged template can return everything that we want - we are not limited to only return strings:
Building tagged templates have an option to also accept variables that can be used:
The first argument is a special object, which behave as an array and provides access to all the "native strings" in the original string that was passed to the tag-template function (strings[0]
, strings[1]
, strings[2]
), alongside a raw
property, which allows you to access the original raw strings.
The rest of the arguments are the expressions that we used inside the template literal.
Time to build - let's start with something basic
Now that we know a bit more about template literals it's time to move on to some more complex examples.
We already know that template literals don't have to return strings, so how about creating a simple example of a tagged template that returns a react component with the relevant style?
We will start by creating a simple div example to just wrap our text with some styling on it:
The tagged template is the following code:
The full example is available here:
Check out the helper functions (the
cssObjFromStr
). we are going to focus on it in the next example.
Using props in the component
The basic example gave us a nice intro, but what about the component's props? We use them all the time in React, and losing them is not an option. Moving to the next example, we will add the option to also use props as part of our component that we would like to style:
We will use the onClick
prop on the div element.
The full example is here:
Using props in the template
Props are not only relevant to the components, but also to the template itself. We want to use the props of the component inside the template itself - colors, elements behavior, and more.
To do this we will need to pass the props from the <Div...>
to the cssObjFromStr
method:
But this is not enough.
Let's assume that we use the <Div>
element with the property textColor="blue"
:
The issue we face here is that the <div>
component (inside the cmp
) will get the textColor
property, which is not a valid property of a div
element.
A specific solution can be to extract the textColor
from the props
, and pass the rest of the properties down to the <div>
element:
Working example can be found here:
The styled-components solution is a bit more elegant (and much more generic) - all props that start with $
are considered "private props" and will not pass down to the actual jsx component.
We will use the same concept, but in our example, we will use the _ (underscore) to create private props.
Full working example can be found here:
Summary
The styled-component library contains much more than that, with the entire built-in HTML tags, wrapped-components, classes and inheritance (instead of inline-style), global themes and more, however styled-components is just an example of how to use the tagged template literals, which is eventually "just" a native javascript feature as of ECMAScript 2015 (also known as ES6).
Cover photo by Gerd Altmann @ pixabay
Top comments (8)
Nice. Note that css-in-js allows non-function expressions. So the
font-size: ${size}px;
expression in the attached image should work too
dev-to-uploads.s3.amazonaws.com/up...
Thanks for mentioning this!
I really impressed with any other CSS-IN-JS methods that Styled uses. There's no need generate weird props and types while using typescript.
Very informative
Thanks!