Tagged Template Literals

 · 3 mins read

I didn’t notice that the back quote’s meaning in the styled-components until today! I have been using styled-components for a long time, and I thought it was Babel somehow to transform the code segment. Today, I finally read that article that explained the magic behind styled-components when I was going through the document online.

No more nonsense, let’s get into our gist.

This is a common pattern of styled-components.

const Button = styled.button`
  background-color: papayawhip;
  border-radius: 3px;
  color: palevioletred;
`

Template literals are enclosed by the back-tick (` `) character instead of double or single quotes. Template literals can contain placeholders. These are indicated by the dollar sign and curly braces (${expression}).

let name = 'Tom';
console.log(`This is ${name}.`);
//output:
//This is Tom.

A more advanced form of template literals are tagged templates. Tags allow you to parse template literals with a function.

The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions.

let func = (strs, ...args) => {console.log(strs);console.log(args);};
func`This is ${name}. How are you?`

// output :
[ 'This is ', '. How are you?' ]  
[ 'Tom' ]

But if we call this function as a normal way,

func(`This is ${name}. How are you?`);

// output :
["This is Tom. How are you?"]
[]

So we only get the first parameter as a string.

How the magic happens?

Let’s craft a function.

let exec = (...args) => args[1](args[0][0]);
let f = str => console.log(str);
exec`hello${f}`;
//output:
//hello

please note the first parameter is an Array!

Do you get it?

We can pass a function as a template string into the tagged expression.

This is called Tagged Template Literals.

In the end,

const Button = styled.button`
  font-size: ${props => props.primary ? '2em' : '1em'};
`

will render:

// font-size: 2em;
<Button primary />

// font-size: 1em;
<Button />

Let’s a more complex example.

const sizes = {
  desktop: 992,
  tablet: 768,
  phone: 576
}

// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {
  acc[label] = (...args) => css`
    @media (max-width: ${sizes[label] / 16}em) {
      ${css(...args)}
    }
  `

  return acc
}, {})

const Content = styled.div`
  height: 3em;
  width: 3em;
  background: papayawhip;

  /* Now we have our methods on media and can use them instead of raw queries */
  ${media.desktop`background: dodgerblue;`}
  ${media.tablet`background: mediumseagreen;`}
  ${media.phone`background: palevioletred;`}
`;

render(
  <Content />
);

Let’s translate it line by line.

//the variable size would become this:
const sizesEquivalent = {
  desktop: (...args) => css`
  @media (max-width: 62em) {
    ${css(...args)}
  }
`
,
  tablet: (...args) => css`
  @media (max-width: 48em) {
    ${css(...args)}
  }
`
,
  phone: (...args) => css`
  @media (max-width: 36em) {
    ${css(...args)}
  }
`
}

And what the hell is css function?

A helper function to generate CSS from atemplate literal with interpolations.

You need to use this if you return a template literal with interpolations inside an interpolation.

It returns an array of interpolations,

which is a flattened data structure that you can pass as an interpolation itself.

console.log(css`color: green;`)
console.log(css`color: ${props => props.color};`)

//["color: green;"]
//["color: ", props => props.color, ";"]

As you can see, if you pass a plain string as a parameter into the function, css just return the plain string in an Array. If you do like tagged template, it will return a array just like we analyzed before.

So, in a conclusion, we would have our component like this:

const ContentEquvalent = styled.div`
  height: 3em;
  width: 3em;
  background: papayawhip;
  @media (max-width: 62em) {
    background: dodgerblue;
  }
  @media (max-width: 48em) {
    background: mediumseagreen;
  }
  @media (max-width: 36em) {
    background: palevioletred;
  }
`;

magic behind styled-components