Skip to content

emotion

SeungAh Hong2min read

Without a React Framework

  • Installation

    npm i @emotion/css
    or
    yarn add @emotion/css
  • Description

    • It doesn't require any additional setup, a babel plugin, or other configuration.
    • It supports auto vendor-prefixing, nested selectors, and media queries.
      • auto vendor-prefixing
        • webkit- supported by OPERA, SAFARI, GOOGLE CHROME
        • moz- supported by FIREFOX
        • ms- supported by EDGE/INTERNET EXPLORER
    • When you use the css function, a classname is generated automatically, and with cx it even supports combining multiple classes.
    • However, additional work is required for server-side rendering.(https://emotion.sh/docs/ssr#api)
    import { css, cx } from '@emotion/css';
     
    const color = 'white';
     
    render(
      <div
        className={css`
          padding: 32px;
          background-color: hotpink;
          font-size: 24px;
          border-radius: 4px;
          &:hover {
            color: ${color};
          }
        `}
      >
        Hover to change color.
      </div>,
    );

With a React Framework

  • @emotion/react

    • Installation

      npm i @emotion/react
      or
      yarn add @emotion/react
    • Description

      • Supports the css prop
        • It supports auto vendor-prefixing, nested selectors, and media queries.
          • auto vendor-prefixing
            • webkit- supported by OPERA, SAFARI, GOOGLE CHROME
            • moz- supported by FIREFOX
            • ms- supported by EDGE/INTERNET EXPLORER
        • It reduces boilerplate code when composing components and applying styles.
      • No separate configuration is needed when performing server-side rendering.
      • We recommend using ESLint to properly apply the recommended patterns and configuration.
      import { css } from '@emotion/react';
       
      const danger = css`
        color: red;
      `;
       
      const base = css`
        background-color: darkgreen;
        color: turquoise;
      `;
       
      render(
        <div>
          <div css={base}>This will be turquoise</div>
          <div css={[danger, base]}>
            This will be also be turquoise since the base styles overwrite the
            danger styles.
          </div>
          <div css={[base, danger]}>This will be red</div>
        </div>,
      );
  • @emotion/styled

    • Installation

      npm i @emotion/styled @emotion/react
      or
      yarn add @emotion/styled @emotion/react
    • Description This is a package for users who prefer a styling approach like styled.div when creating components.

      import styled from '@emotion/styled';
       
      const Button = styled.button`
        padding: 32px;
        background-color: hotpink;
        font-size: 24px;
        border-radius: 4px;
        color: black;
        font-weight: bold;
        &:hover {
          color: white;
        }
      `;
       
      render(<Button>This my button component.</Button>);
    • Styling elements and components

      import styled from '@emotion/styled';
       
      const Button = styled.button`
        color: turquoise;
      `;
       
      render(<Button>This my button component.</Button>);
    • Changing based on props When you want to change a component's styles based on props.

      import styled from '@emotion/styled';
       
      const Button = styled.button`
        color: ${props => (props.primary ? 'hotpink' : 'turquoise')};
      `;
       
      const Container = styled.div(props => ({
        display: 'flex',
        flexDirection: props.column && 'column',
      }));
       
      render(
        <Container column>
          <Button>This is a regular button.</Button>
          <Button primary>This is a primary button.</Button>
        </Container>,
      );
    • classname styling through component inheritance

      import styled from '@emotion/styled';
       
      const Basic = ({ className }) => <div className={className}>Some text</div>;
       
      const Fancy = styled(Basic)`
        color: hotpink;
      `;
       
      render(<Fancy />);
    • When you want to keep the styles but change only the element Using withComponent, you can carry over the styles as they are while changing only the element. You can also change the element using the as property.

      import styled from '@emotion/styled';
       
      const Section = styled.section`
        background: #333;
        color: #fff;
      `;
       
      // this component has the same styles as Section but it renders an aside
      const Aside = Section.withComponent('aside');
       
      render(
        <div>
          <Section>This is a section</Section>
          <Aside>This is an aside</Aside>
        </div>,
      );
       
      import styled from '@emotion/styled';
       
      const Button = styled.button`
        color: hotpink;
      `;
       
      render(
        // button -> a tag로 변경
        <Button as="a" href="https://github.com/emotion-js/emotion">
          Emotion on GitHub
        </Button>,
      );
    • When you need to target a component to change its CSS You can change the styles by using @emotion/babel-plugin.

      // emotion string styles
      import styled from '@emotion/styled';
       
      const Child = styled.div`
        color: red;
      `;
       
      const Parent = styled.div`
        ${Child} {
          color: green;
        }
      `;
       
      render(
        <div>
          <Parent>
            <Child>Green because I am inside a Parent</Child>
          </Parent>
          <Child>Red because I am not inside a Parent</Child>
        </div>,
      );
       
      // emotion object styles
      import styled from '@emotion/styled';
       
      const Child = styled.div({
        color: 'red',
      });
       
      const Parent = styled.div({
        [Child]: {
          color: 'green',
        },
      });
       
      render(
        <div>
          <Parent>
            <Child>green</Child>
          </Parent>
          <Child>red</Child>
        </div>,
      );
    • When you need to forward props conditionally When the shouldForwardProp condition evaluates to true, the prop is not forwarded during rendering.

      import isPropValid from '@emotion/is-prop-valid'
      import styled from '@emotion/styled'
       
      const H1 = styled('h1', {
        // shouldForwardProp 모두 true로 줄 경우 children props로 전달이 안되서 화면이 안보이게 됩니다.
      / // default as/children 순차적으로 호출됨.
        shouldForwardProp: prop => isPropValid(prop) && prop !== 'color'
      })(props => ({
        color: props.color
      }))
       
      render(<H1 color="lightgreen">This is lightgreen.</H1>)
    • When you need to apply dynamic styles

      import styled from '@emotion/styled';
      import { css } from '@emotion/react';
       
      const dynamicStyle = props => css`
        color: ${props.color};
      `;
       
      const Container = styled.div`
        ${dynamicStyle};
      `;
      render(<Container color="lightgreen">This is lightgreen.</Container>);
    • Nesting components

      import styled from '@emotion/styled';
       
      const Example = styled('span')`
        color: lightgreen;
       
        & > strong {
          color: hotpink;
        }
      `;
       
      render(
        <Example>
          This is <strong>nested</strong>.
        </Example>,
      );
  • Media Queries

    • Basics

      import { css } from '@emotion/react';
       
      render(
        <p
          css={css`
            font-size: 30px;
            @media (min-width: 420px) {
              font-size: 50px;
            }
          `}
        >
          Some text!
        </p>,
      );
    • Reusing media queries

      import { css } from '@emotion/react';
       
      const breakpoints = [576, 768, 992, 1200];
       
      const mq = breakpoints.map(bp => `@media (min-width: ${bp}px)`);
       
      render(
        <div>
          <div
            css={
              color: 'green',
              [mq[0]]: {
                color: 'gray',
              },
              [mq[1]]: {
                color: 'hotpink',
              },
            }
          >
            Some text!
          </div>
          <p
            css={css`
              color: green;
              ${mq[0]} {
                color: gray;
              }
              ${mq[1]} {
                color: hotpink;
              }
            `}
          >
            Some other text!
          </p>
        </div>,
      );
    • Using facepaint

      • Installation
        npm i facepaint
        or
        yarn add facepaint
      import facepaint from 'facepaint';
       
      const breakpoints = [576, 768, 992, 1200];
       
      const mq = facepaint(breakpoints.map(bp => `@media (min-width: ${bp}px)`));
       
      render(
        <div
          css={mq({
            color: ['green', 'gray', 'hotpink'],
          })}
        >
          Some text.
        </div>,
      );
  • Global Styles

    import { Global, css } from '@emotion/react';
     
    render(
      <div>
        <Global
          styles={css`
            .some-class {
              color: hotpink !important;
            }
          `}
        />
        <Global
          styles={
            '.some-class': {
              fontSize: 50,
              textAlign: 'center',
            },
          }
        />
        <div className="some-class">This is hotpink now!</div>
      </div>,
    );
  • Keyframes

    import { css, keyframes } from '@emotion/react';
     
    const bounce = keyframes`
      from, 20%, 53%, 80%, to {
        transform: translate3d(0,0,0);
      }
     
      40%, 43% {
        transform: translate3d(0, -30px, 0);
      }
     
      70% {
        transform: translate3d(0, -15px, 0);
      }
     
      90% {
        transform: translate3d(0,-4px,0);
      }
    `;
     
    render(
      <div
        css={css`
          animation: ${bounce} 1s ease infinite;
        `}
      >
        some bouncing text!
      </div>,
    );
  • Applying styles globally to a component (spread operator)

    import { css } from '@emotion/react';
     
    const pinkInput = css`
      background-color: pink;
    `;
    const RedPasswordInput = props => (
      <input
        type="password"
        css={css`
          background-color: red;
          display: block;
        `}
        {...props}
      />
    );
     
    render(
      <div>
        <RedPasswordInput placeholder="red" />
        <RedPasswordInput placeholder="pink" css={pinkInput} />
      </div>,
    );
  • Applying a theme When wrapped with a ThemeProvider, the theme is shared through the Context API, so it can be used in child components.

    // css prop
    import { ThemeProvider } from '@emotion/react';
     
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
     
    render(
      <ThemeProvider theme={theme}>
        <div css={theme => ({ color: theme.colors.primary })}>
          some other text
        </div>
      </ThemeProvider>,
    );
     
    // styled
    import { ThemeProvider } from '@emotion/react';
    import styled from '@emotion/styled';
     
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
     
    const SomeText = styled.div`
      color: ${props => props.theme.colors.primary};
    `;
     
    render(
      <ThemeProvider theme={theme}>
        <SomeText>some text</SomeText>
      </ThemeProvider>,
    );
     
    // useTheme hook
    import { ThemeProvider, useTheme } from '@emotion/react';
     
    const theme = {
      colors: {
        primary: 'hotpink',
      },
    };
     
    function SomeText(props) {
      const theme = useTheme();
      return <div css={ color: theme.colors.primary } {...props} />;
    }
     
    render(
      <ThemeProvider theme={theme}>
        <SomeText>some text</SomeText>
      </ThemeProvider>,
    );
  • Exposing the className as a value (using labels)

    import { css } from '@emotion/react';
     
    let style = css`
      color: hotpink;
      label: some-name;
    `;
     
    let anotherStyle = css({
      color: 'lightgreen',
      label: 'another-name',
    });
     
    let ShowClassName = ({ className }) => (
      <div className={className}>{className}</div>
    );
     
    render(
      <div>
        <ShowClassName css={style} />
        <ShowClassName css={anotherStyle} />
      </div>,
    );
  • When you need to use css and cx together (using ClassNames)

    import { ClassNames } from '@emotion/react';
     
    // this might be a component from npm that accepts a wrapperClassName prop
    let SomeComponent = props => (
      <div className={props.wrapperClassName}>
        in the wrapper!
        <div className={props.className}>{props.children}</div>
      </div>
    );
     
    render(
      <ClassNames>
        {({ css, cx }) => (
          <SomeComponent
            wrapperClassName={css({ color: 'green' })}
            className={css`
              color: hotpink;
            `}
          >
            from children!!
          </SomeComponent>
        )}
      </ClassNames>,
    );
  • When you need to specify a key value (using CacheProvider)

    import { CacheProvider, css } from '@emotion/react';
    import createCache from '@emotion/cache';
    import { prefixer } from 'stylis';
     
    const customPlugin = () => {};
     
    const myCache = createCache({
      key: 'my-prefix-key',
      stylisPlugins: [
        customPlugin,
        // has to be included manually when customizing `stylisPlugins` if you want
        // to have vendor prefixes added automatically
        prefixer,
      ],
    });
     
    render(
      <CacheProvider value={myCache}>
        <div // class: my-prefix-key-zjik7
          css={css`
            display: flex;
          `}
        >
          <div // class: my-prefix-key-yc3urw
            css={css`
              flex: 1;
              transform: scale(1.1);
              color: hotpink;
            `}
          >
            Some text
          </div>
        </div>
      </CacheProvider>,
    );

Using the Babel Plugin

  • Installation

    npm i -D @emotion/babel-plugin
    or
    yarn add -D @emotion/babel-plugin
  • .babelrc

    {
      "plugins": ["@emotion"]
    }
     
    // when you need separate settings per env environment
    {
      "env": {
        "production": {
          "plugins": ["@emotion", ...otherBabelPlugins]
        }
      },
      "plugins": ["@emotion"]
    }

Browser Support

  • emotion supports most browsers, including IE11.

References

Introduction