Emotion: Emotion 9 to 10 - how to pass external classes properly

Created on 25 Jan 2019  路  9Comments  路  Source: emotion-js/emotion

This is maybe a question about design rather than a specific bug.

  • emotion version: 10.0.6
  • react version: 16.4.2

Relevant code:

Previous emotion 9 code:

let carousel = css`
.. some carousel styles
`;

let dots = css`
..some dots styles
`;

<div className={cx(carousel, className)}>
      <Slider dotsClass={dotsClassName || dots}>
        {children}
      </Slider>
</div>

className and dotsClassName are custom class names you can pass down to the component to customize it.

What you did:

Now with emotion 10 im not even sure how I would write this in a clean and concise way.
I did get it to work with this:

let carousel =`
.. some carousel styles
`;

let dots = `
..some dots styles
`;

<ClassNames>
    {({ css, cx }) => (
      <div
        className={cx(
          css`${carousel};`,
          className,
        )}
      >
        <Slider
          dotsClass={dotsClassName || css`${dots};`}
        >
          {children}
        </Slider>
      </div>
    )}
  </ClassNames>

But this seems like a really cumbersome / hard to read with ClassNames (alot more jsx and harder to follow imo)

I know its probably not the best practice to be passing class names to components (anti pattern) but in this case Slider is a 3rd party component and the only way to customise its styles is to pass down a class name.

Suggested solution:

Not sure - maybe there is a better way to write the above that im missing from the docs.

Most helpful comment

Just import css and cx from 'emotion' instead of '@emotion/core'.

It returns the name of the class.

All 9 comments

Facing the same here, that's a really ugly approach and I agree with your points. Sailing through de docs I couldn't figure out a better way to solve it. Now I got something like when styling a disabled button:

in style.js:

let bigStyle1 =`
.....
`;

let disabledStyle = `
.....
`;

in my component return: 

<ClassNames>
    {({ css, cx }) => {

      const buttonStyle = cx(
        css`${bigStyle1}`,
        {[css`${disabledStyle}`]: disabled}
      )

      return (
        <button
          type={type}
          disabled={disabled}
          onClick={onClick}
          className={buttonStyle}
          {...props}
        >
          {text}
        </button>
      )
    }}
  </ClassNames>

@lucasgdev - This issue involves passing a proprietary prop to a 3rd party component that expects a CSS className (e.g. dotsClass from the Slider component).

I think you still have the straightforward case of styling an HTML button with multiple style definitions and can pass an array to the css prop as follows. When disabled is false, the second array value (false) is ignored by emotion.

return (
  <button
    type={type}
    disabled={disabled}
    onClick={onClick}
    css={[bigStyle1, disabled && disabledStyle]}
    {...props}
  >
    {text}
  </button>
)

Just import css and cx from 'emotion' instead of '@emotion/core'.

It returns the name of the class.

@acnebs can do that but im no longer using that dependancy - only installed @emotion/core as per docs
Prefer not to use the legacy emotion lib

The proper way to pass classes to 3rd party components is to use the ClassNames component from @emotion/core.

<ClassNames>
  {({ css }) => {
    const classNameUsableOn3rdParty = css`color: red;`;
    return <Foo someClassName={classNameUsableOn3rdParty} />
  }}
</ClassNames>

@FezVrasta The purpose of this issue is that this is a very clunky solution in many use-cases. I don't want to define my css in-line except in the rarest of cases, but the <ClassNames /> solution requires you to define your css in your render.

I'm sorry I didn't understand that was the issue's purpose, reopening 馃憣

Something I do very often is this:

<ClassNames>
  {({ css, foo = css`color: red;` }) =>
    <Foo someClassName={foo} />
  }
</ClassNames>

But it has issues with Flow types because the signature is invalid, I opened a PR to fix that but it's stuck.
https://github.com/emotion-js/emotion/pull/1324

I would propose to close this and focus on a React Hook solution as proposed in this issue. What do you think?

I don't want to define my css in-line except in the rarest of cases, but the solution requires you to define your css in your render.

@acnebs this is not quite true - you can define your styles outside of render and only call css with them as an argument inside the render callback, like here: https://codesandbox.io/s/emotion-z8jpe

That being said - we'll rethink how this problem can be handled right now. We are working on a v11, so we'd like to address problems like this. The solution is not obvious - so no promises yet, but please know that we are going to think about this.

@lucasgdev - This issue involves passing a proprietary prop to a 3rd party component that expects a CSS className (e.g. dotsClass from the Slider component).

I think you still have the straightforward case of styling an HTML button with multiple style definitions and can pass an array to the css prop as follows. When disabled is false, the second array value (false) is ignored by emotion.

return (
  <button
    type={type}
    disabled={disabled}
    onClick={onClick}
    css={[bigStyle1, disabled && disabledStyle]}
    {...props}
  >
    {text}
  </button>
)

Thanks @ryanswanson I was looking how to do this conditional classes based in boolean props and you save me! ;)

Was this page helpful?
0 / 5 - 0 ratings