Media Queries in Emotion Styled Components
Asked Answered
L

2

9

The Emotion docs tell us how to make reusable media queries that works in the css prop. This allows us to make the following queries in a css prop:

<div
  css={{
    color: 'green',
    [mq[0]]: {
      color: 'gray'
    },
    [mq[1]]: {
      color: 'hotpink'
    }
  }}
>

With mq[0] and mq[1] referring to the first two items in a breakpoints array. For example: const breakpoints = [576, 768, 992, 1200].

What's more, this article takes it one step further, by showing up how to get named reusable media queries by using a breakpoint object. It starts by making a similar function as per the emotion docs, but for objects:

const mq = n => {
  const bpArray = Object.keys(bp).map(key => [key, bp[key]]);

  const [result] = bpArray.reduce((acc, [name, size]) => {
    if (n === name) return [...acc, `@media (min-width: ${size}px)`];
    return acc;
  }, []);

  return result;
};

This then allows us to create a breakpoints object with named media queries:

// object
const breakpoints = {
  sm: 500,
  md: 768,
  lg: 992,
  xl: 1200
};


// query
${mq('sm')} { 
  color: gray;
}

So far, so good.

I would now like to do something similar in an emotion styled component. As such, I created an breakpoints object and the same function as mentioned in the above article.

I then tried to use the short hand media query in my emotion styled component -- like this:

import styled from '@emotion/styled'

const Container = styled.div`
  ${mq('sm')`max-width: 750px;`}
  ${mq('md')`max-width: 970px;`}
  ${mq('lg')`max-width: 1170px`}
`

But when I try this, it does not work. I get the following error message:

TypeError: Object(...) is not a function

Any idea why this is happening and what I can do to get it to work?

Thanks.

Lam answered 1/1, 2020 at 12:0 Comment(2)
${mq('sm')} {max-width: 750px;}?Bridgers
Thanks -- that did it.Lam
H
6

To clarify, there was mainly a minor syntax error in what the OP had posted (there should be no additional backticks in the interpolated string).

A full example of his code including type annotations would look like:

const breakpoints: { [index: string]: number } = {
  sm: 500,
  md: 768,
  lg: 992,
  xl: 1200,
};

const mq = Object.keys(breakpoints)
  .map((key) => [key, breakpoints[key]] as [string, number])
  .reduce((prev, [key, breakpoint]) => {
    prev[key] = `@media (min-width: ${breakpoint}px)`;
    return prev;
  }, {} as { [index: string]: string });

const Container = styled.div`
  ${mq["sm"]} {
    max-width: 750px;
  }
  ${mq["md"]} {
    max-width: 970px;
  }
  ${mq["lg"]} {
    max-width: 1170px;
  }
`;
Hellhound answered 13/12, 2020 at 22:31 Comment(0)
F
0

The @bluenote10 answer is correct. Here's a boosted method if you want to add auto-completion with your IDE in your css definition.

export const BREAKPOINTS = {
  xs: 420,
  sm: 576,
  md: 768,
  lg: 900,
  xl: 1200,
  xxl: 1536
};

type Mq = keyof typeof BREAKPOINTS;
export const mql = Object.keys(BREAKPOINTS)
  .map((key) => [key, BREAKPOINTS[key as Mq]] as [Mq, number])
  .reduce((prev, [key, breakpoint]) => {
    prev[key] = `@media (min-width: ${breakpoint}px)`;
    return prev;
  }, {} as Record<Mq, string>);
Fm answered 19/1, 2022 at 11:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.