Emotion: Emotion v10 unusable with pure TypeScript

Created on 28 Nov 2018  ยท  78Comments  ยท  Source: emotion-js/emotion

  • emotion version: 10.0.0
  • react version: 16.6.3
  • typescript version: 3.1.6

Relevant code.

const flexContainer = css`
  display: flex;
  justify-content: space-between;
`;

What you did:
I've tried to use it as:

<div className={flexContainer.styles} />

Or:

<div css={flexContainer} />

What happened:
I've got this in html:

<div class="
  display: flex;
  justify-content: space-between;
"></div>

Or:

<div css="[object Object]"></div>

I'm using only TypeScript without babel.

Problem description:
How can I use css or className with emotion v10 and TypeScript?

question

Most helpful comment

FYI having "jsxFactory": "jsx" means that you would have to add import { jsx } from '@emotion/core'; to every single React component.
To be honest I'm really sad about this change (jsx pragma in every file you need css prop) in Emotion 10 ๐Ÿ‘Ž.

All 78 comments

@krzysztofzuraw Do you have jsx pragma? It's hard to say without reproducing example.

@TrySound sure - I will prepare codesandbox ๐Ÿ˜„

@TrySound Codesandbox example: https://codesandbox.io/s/mm0pymo5v8

Working example: https://codesandbox.io/s/2j01mr3m1y . I've added "jsxFactory": "jsx", option, import { jsx } from @emotion/core and jsx pragma (/** @jsx jsx */). This probably can be simplified - I just dont know how to automate it.

Type checking css prop doesnt quite work on this sandbox. Probably @Ailrun will know what to do about it.

FYI having "jsxFactory": "jsx" means that you would have to add import { jsx } from '@emotion/core'; to every single React component.
To be honest I'm really sad about this change (jsx pragma in every file you need css prop) in Emotion 10 ๐Ÿ‘Ž.

With babel this all can be automated - no need for any manual wiring up. Im just not that experienced with TS to know how to achieve the same with it (I suspect there is one though).

I see, that means no jsx pragma with the plugin? I haven't found this information in the docs, sorry โ˜น๏ธ. I'm afraid that with pure TypeScript compiler there's only the js pragma way or jsxFactory option but that would mean the import as I mentioned earlier.

@Andarist
TS does not support typechecking based on JSX pragma (we cannot change typing when users change JSX pragma), so current TS typing just have css props when users import @emotion/core.
And this issue actually is not about TS, but about the way to use emotion@10, since @krzysztofzuraw missed JSX pragma, and used className instead of css.

So it should just work when importing from @emotion/core, right?
On Fri, 7 Dec 2018 at 17:18, Junyoung Clare Jang notifications@github.com
wrote:

@Andarist https://github.com/Andarist
TS does not support typechecking based on JSX pragma (we cannot change
typing when users change JSX pragma), so current TS typing just have css
props when users import @emotion/core.
And this issue actually is not about TS, but about the way to use
emotion@10, since @krzysztofzuraw https://github.com/krzysztofzuraw
missed JSX pragma, and used className instead of css.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/emotion-js/emotion/issues/1046#issuecomment-445283549,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AJWMkhpNIGGNpcWuEHDmltQJ3QnfI9QEks5u2pTpgaJpZM4Y3prj
.

Type check will work with imported @emotion/core, but since emotion@10 requires JSX pragma to be executed, user should put JSX pragma.

Whilst not pretty, a temporary solution is to state jsx; after declaring the pragma
(no need to set "jsxFactory" and import in each file)

/** @jsx jsx */ jsx;

For example

/** @jsx jsx */ jsx;
import * as React from "react";
import { jsx, css } from "@emotion/core";

const yolo = css({
  color: "lightcoral"
});

and then use like so

<p css={yolo}>swaggins</p>

I am struggling to use the css prop and jsx pragma with typescript (using CRA) and I get ReferenceError: jsx is not defined.

/** @jsx jsx */ jsx
import { jsx, css } from '@emotion/core'

@joe-bell this fixes it for me, but aren't you're still having to declare the jsx pragma in each file?

  1. If you don't want to repeat JSX pragma, use "jsxFactory" option with "jsx": "React" option.
  2. If you want not to change default jsx factory which is "React.createElement", you can put /** @jsx jsx */ in files those are also importing jsx function and using emotion.
    See https://www.typescriptlang.org/docs/handbook/jsx.html

@bitttttten yes, but only in the files where you _need_ to enable it! Not every file.

@Ailrun Unfortunately with 2, this doesn't seem to work for unless you call jsx; somewhere in the file.

Did you mean webpack treeshakes jsx? Could you give me more info?

@Ailrun sure, in this instance I'm compiling using tsc.

If I use:

/** @jsx jsx */;
import { css, jsx } from "@emotion/core";

No errors appear in the console during compile time but when I consume the outputted lib in Docz I'm presented with a Webpack error page of "ReferenceError: jsx is not defined"

However, if I add jsx; to the end of the first line pragma comment, everything works perfectly.


Environment:


I'd love to be able to offer some support to investigate this issue further but I'm fairly new to TypeScript

Could you give me the result (the output of the compilation of lib) for a single component with the problem? It seems a TS issue or a Webpack issue to me...

Using:

/** @jsx jsx */
import { css, jsx } from '@emotion/core'
import { FunctionComponent } from 'react'

const App: FunctionComponent<{ text: string }> = ({ text }) => {
    return (
        <div
            css={css`
                text-align: center;
            `}
        >
            {text}
        </div>
    )
}

Auto saves into

/** @jsx jsx */
import { css } from '@emotion/core'
import { FunctionComponent } from 'react'

const App: FunctionComponent<{ text: string }> = ({ text }) => {
    return (
        <div
            css={css`
                text-align: center;
            `}
        >
            {text}
        </div>
    )
}

(jsx is removed because auto save cleans up unused variables)

It gives, in the browser:

ReferenceError: jsx is not defined
App
src/App.tsx:7
   5 | 
   6 | const App: FunctionComponent<{ text: string }> = ({ text }) => {
>  7 |     return (
   8 |         <div
   9 |             css={css`
  10 |                 text-align: center;

Full dump here: https://pastebin.com/1EBkHRLN

tsconfig is:

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "preserve"
    },
    "exclude": [
        "node_modules",
        "build",
        "scripts",
        "public",
        "src/setupTests.ts"
    ],
    "include": ["src"]
}

and eslintrc.json:

{
    "extends": ["airbnb", "prettier"],
    "plugins": ["prettier"],
    "rules": {
        "prettier/prettier": ["error"]
    },
    "parser": "babel-eslint",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true,
            "modules": true
        }
    }
}

FWIW the emotion eslint plugins will automatically add pragma and import for you when using the ESLint --fix option.

https://github.com/emotion-js/emotion/blob/master/packages/eslint-plugin-emotion/docs/rules/jsx-import.md

Check out the others here: https://github.com/emotion-js/emotion/tree/master/packages/eslint-plugin-emotion

I'm not sure how this will interact with typescript but it removes the effort of using the pragma import. I just start typing and on save its added for me and I don't think about it again.

@bitttttten thanks for the example. I'm going to ask around.

@bitttttten Which editor do you use? Does you editor also remove React import from import React from 'react' if you don't use React directly? If not, is it impossible to set your editor not to remove jsx too?

This is the relevant ESLint rule: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md

And its docs say that it should work with the pragma comment:

If you are using the @jsx pragma this rule will mark the designated variable and not the React one.

TypeScript will not auto-remove unused variables or imports unless you tell it to (it's not part of its downcompile). @joe-bell can you share your tsconfig.json and what the before/after code looks like?

@ailrun apologies for the delay, I'm going to need to try to re-create this issue without using my client's code. Will try to give you some more details on this soon

Which editor do you use?

VSCode

Does you editor also remove React import from import React from 'react' if you don't use React directly?

No!

/** @jsx jsx */
import { jsx, css } from '@emotion/core'
import React, { FunctionComponent } from 'react'

Gets turned into:

/** @jsx jsx */
import { css } from '@emotion/core'
import React, { FunctionComponent } from 'react'

If not, it is impossible to set your editor not to remove jsx too?

That's why I came into this thread.. hehe. I even tried installing eslint-plugin-emotion although it seems to have no effect.

I made a repo for this issue, which is just a CRA app. The file I have been talking about is here.

Did you mean webpack treeshakes jsx? Could you give me more info?

I'm guessing that the people who are having this issue where adding jsx; to the start of the file fixes this are compiling using babel, and the typescript babel plugin removes imports that aren't used in a value position because it is assumed that they are type-only imports. This is different from the way that tsc compiles TypeScript, because babel has the limitation that it only looks at a single file when compiling.

I'll open an issue on the babel repo.

EDIT: There already is an issue opened, and the fix is to set the jsxPragma option in the typescript transform.

So it's not possible to fix this when using CRA until the latest fix in babel has been released.

The fix in Babel has been released - if you reinstall your dependencies it should fix this particular problem.

I updated my deps but now I am finding that React is being removed when not explicity defined when using jsx. Maybe I will wait for the CRA release :)

@Andarist I've upgraded the dependencies to the latest version but it is still compiling [object object].

I updated my deps but now I am finding that React is being removed when not explicity defined when using jsx. Maybe I will wait for the CRA release :)

Could you give an example? Not sure what do you mean by that and what kind of a problem it produces.

@Andarist I've upgraded the dependencies to the latest version but it is still compiling [object object].

I was referring to the other problem - you most likely use css from @emotion/core without using custom jsx pragma.

@Andarist thanks for the quick reply. The boilerplate code required to use css is very frustrating. Is there a fix on the way?

It's a little bit poblematic with TS - and there are no plans to "fix" this as fixing this would break TS ability to type check css prop.

It's being possible to workaround this boilerplate when writing vanilla JS with babel/eslint, but I guess this is not what you are after.

Truth to be told the "boilerplate" you are mentioning is one extra line (jsx pragma), is this really that much boilerplate to write this?

/** @jsx jsx */
import { jsx, css } from '@emotion/core'

You might suffer from really similar boilerplate when using any other library as you are obliged to specify which imports do you use (or import whole namespace).

I am seeing the same thing as @bitttttten after upgrading to the latest Babel (which fixed the jsx import being removed when the pragma was present).

In a component that uses Emotion, I have:

/** @jsx jsx */
import { jsx, css } from "@emotion/core"
import * as React from "react"
import styled from "@emotion/styled"
โ€ฆ

and it works fine.

But in a component without Emotion, I get a ReferenceError: React is not defined.
In my original file, I have those imports:

import * as React from "react"
import { connect } from "react-redux"

import ReactResizeDetector from "react-resize-detector"
โ€ฆ

After Babel transpilation, it looks correct:

import _styled from "@emotion/styled-base";

(function () {
  var enterModule = require('react-hot-loader').enterModule;

  enterModule && enterModule(module);
})();

import * as React from "react";
import { connect } from "react-redux";
import ReactResizeDetector from "react-resize-detector";
โ€ฆ

But it looks like React is treeshaked by Webpack and not available in the final bundle:

/*!********************************************************!*\
  !*** ./app/client/components/chapter/index.tsx ***!
  \********************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* WEBPACK VAR INJECTION */(function(module) {/* harmony import */ var _emotion_styled_base__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @emotion/styled-base */ "./node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js");
/* harmony import */ var react_redux__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-redux */ "./node_modules/react-redux/es/index.js");
/* harmony import */ var react_resize_detector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! react-resize-detector */ "./node_modules/react-resize-detector/lib/esm/index.js");

(function () {
  var enterModule = __webpack_require__(/*! react-hot-loader */ "../react-hot-loader/index.js").enterModule;

  enterModule && enterModule(module);
})();

I do not understand why the React import is no longer here.

Truth to be told the "boilerplate" you are mentioning is one extra line (jsx pragma), is this really that much boilerplate to write this?

@Andarist I've been using Emotion since July now and I love it, it's completely transformed my perspective on CSS in JS, but I'm starting to find the css prop a mixed blessing - this is a really good example why.

Emotion's always been a winner for me as the tool that 'works out of the box', but using the custom pragma, in reality, is often not as simple as adding this one extra line. TypeScript compiler (particularly with project references) and Docz for instance, are two examples which are causing headaches right now. It's sadly not possible to use without.

I really appreciate the hard work you maintainers are doing to keep this project going, but I think it could be worth taking another look at the custom pragma and opening it up for discussion to the wider community. I would be more than happy to get involved with such a thing

I'm super disappointed that emotion is not usable with my setup (fuse-box + typescript, no babel). Version 9 exported the css function that I could use wherever I wanted. My components looked super clean:

export default () => (
  <div className={css({ backgroundColor: 'red' })} />
)

This was completely agnostic to build system and frontend framework, and it was the greatest advantage over styled-components. Now, I can only use emotion like styled-components by introducing new components everywhere using div({backgroundColor: 'red'}) or by adding babel which I really don't want. Guess I need to get back to fela, their new hooks API is much closer to my preferred use case https://github.com/rofrischmann/fela/pull/648, or just stick to emotion@9 and hope we will get the old css function back.

You can use emotion in TS-only environment. TS does support JSXFactory and /* @jsx jsx */ pragma.

@bkniffler and if you want to keep using "old" css you can just import it from emotion which should work fine if that's all you need

import { css } from 'emotion'

export default () => (
  <div className={css({ backgroundColor: 'red' })} />
)

You can use emotion in TS-only environment. TS does support JSXFactory and /* @jsx jsx */ pragma.

@Ailrun You can use JSXFactory or /*@jsx jsx*/ but then you cannot use React.Fragments.
After adding pragma to the to file I want to use <></> and I got: JSX fragment is not supported when using an inline JSX factory pragma error from TypeScript compiler.

@Andarist Thx for that. I couldn't find that piece of info anywhere else. I'm confused that @emotion/core + @emotion/react have different css export compared to emotion.

@Ailrun You can use JSXFactory or /*@jsx jsx*/ but then you cannot use React.Fragments.
After adding pragma to the to file I want to use <></> and I got: JSX fragment is not supported when using an inline JSX factory pragma error from TypeScript compiler.

Seems like this should be fixed in TS compiler then - by providing a way to customize fragment pragma, similar to https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#pragmafrag .

@Andarist Thx for that. I couldn't find that piece of info anywhere else. I'm confused that @emotion/core + @emotion/react have different css export compared to emotion.

I agree the naming is confusing, not sure how to fix it though. @emotion/core's css reaches for a emotion cache object kept in React's context, that's why it works differently from emotion's css (i.e. cant be just used as string class name), whereas emotion's css has similar cache but it's kept "globally" in a form of singleton.

IMHO the docs should begin with stating that the core of emotion is a naked emotion css function that's usable in any environment (frontend framework / build tool), and describe how it can be used.

Maybe a simple overview about every way emotion can be used:

/* ********** CHOOSE YOUR PATH ************ */
/* The purist */
function Component({ color }) {
  // JS way
  return (
    <div className={css({ color: 'red' })}>hi</div>
  )
  // CSS way
  return (
    <div className={css(`color: red`)}>hi</div>
  )
}

/* The hooker */
function Component({ color }) {
  // JS way
  const className = useCSS({ color: 'red' })
  return (
    <div className={className)}>hi</div>
  )
  // CSS way
  const className = useCSS(`color: red`)
  return (
    <div className={className}>hi</div>
  )
}

/* The babelist */
function Component() {
  // JS way
  return (
    <div css={css({ color: 'red' })}>hi</div>
  )
  // CSS way
  return (
    <div css={css(`color: red`)}>hi</div>
  )
}

/* The compononist */
// JS way
const Div = styled.div({
  color: 'red'
})
// CSS way
const Div = styled.div`
  color: red
`
function Component() {
  return (
    <Div>hi</Div>
  )
}

Maybe there could be a landing page with different buttons to switch between those and js-way/css-way, and their advantages/disadvantages very shortly.

IMHO the docs should begin with stating that the core of emotion is a naked emotion css function that's usable in any environment (frontend framework / build tool), and describe how it can be used.

It's mentioned to some extent in the introduction - https://emotion.sh/docs/introduction#framework-agnostic

Adding the pragma /** @jsx jsx */ helps, but I've been running into periodic 'React is not defined errors' after adding the pragma too. In order to get things working again, I've had to add the pragma and imports to components that don't use emotion directly but import components that do. In my case, I'm using vscode with typescript and create react app.

Can confirm that I have been having exactly the same problem. Also CRA with typescript.

Same problem too @fourlincoln10 and @MargaretKrutikova . I thought it was just me. Stopping the webpack dev server, removing node_modules/.cache, and then restarting my webpack dev server fixed the issues for me.

Maybe this issue should be in another thread? As it seems unrelated to typescript.

Chiming in with the same problem using Typescript and Create React App. Any movement to fix this? Or at least mention it in the docs somewhere?

@bitttttten someone appears to have created an issue for this specific problem with the same workaround here.

Is there any chance that this issue would be prioritized and fixed? Or we've to rely on /** @jsx jsx */ forever? ๐Ÿ˜

Are you not able to use @emotion/babel-preset-css-prop @shahzaibkhalid ?

@bitttttten I'm using @emotion/babel-preset-css-prop but I'm getting <div css="[object Object]"></div>, however @emotion/styled works super fine.

@bitttttten and for everyone else,

I did one experiment, I was using both babel-loader and ts-loader, when I removed ts-loader and only used babel-loader to handle TypeScript, it started working. ๐Ÿ˜ƒ

Which issue were you having @shahzaibkhalid ? Just to clarify, since there are a few different ones mentioned here.

@bitttttten I had issues with CSS Prop only in a TypeScript application.

Here's detail of package:

@emotion/core: 10.0.10
@emotion/babel-preset-css-prop: 10.0.9

For example, if I apply some styles to a div, using CSS Prop, emotion won't apply the styles and while inspecting in browser, I'd see something like <div css="[object Object]">.

If I use /** @jsx jsx */ it works fine, but as I already had @emotion/babel-preset-css-prop properly configured and same setting worked for me in JavaScript React project, so I tried various tricks. The only thing that worked for me was to handle TypeScript code with Babel and remove ts-loader in favor of babel-loader, as soon as I did that. CSS Prop issue of <div css="[object Object]"> gone away

Yeah similar issue here too ๐Ÿ‘‹

I've implemented the following eslint (react/jsx-fragments) rule to make sure that React.Fragment is used over <> as a mid-term solution

@peduarte that's a useful rule! unfortunately TS compiler doesn't support shorthand fragment syntax in combination with custom jsx pragma. That's worth reporting to them.

The bottom line of this issue is that emotion is usable with TS, but you have to use custom jsx pragma and can't use <></> syntax (<Fragment></Fragment> has to be used). We can't do much in emotion itself to address those issues (which really are not that bad), so I feel like closing this issue is the appropriate thing to do. We should add a docs section about this (hint: we'd appreciate a PR from the community).

If you still have some with setting up emotion with TS please open new issues as this one got already really long and lost its original focus.

While we are here it would make sense to verify if the same issue happens with Flow so that we can document both.

The bottom line of this issue is that emotion is usable with TS, but you have to use custom jsx pragma and can't use <> syntax ( has to be used).

@Andarist What does that "custom jsx pragma" look like? Do you just mean /** @jsx jsx */? Can't you also just get around the jsx pragma using the @emotion/babel-preset-css-prop plugin?

@Andarist What does that "custom jsx pragma" look like? Do you just mean /** @jsx jsx */?

Yes.

Can't you also just get around the jsx pragma using the @emotion/babel-preset-css-prop plugin?

Yes, you can - but the issue here was about usage with TS compiler, not babel.

WTH ๐Ÿ˜คI've spent hours and this nasty [object Object] thing is not going away anyhow in my typescript react project.
Does anyone know a solution to this sad problem??

I know this isn't helpful, but I ended up switching back to Styled Components :(

@paramsinghvc I'm using Parcel with the following configuration and it works as expected.

.babelrc

{
  "plugins": [
    ["transform-inline-environment-variables"],
    ["babel-plugin-jsx-pragmatic", { "export": "jsx", "module": "@emotion/core", "import": "___EmotionJSX" }],
    ["@babel/plugin-transform-react-jsx", { "pragma": "___EmotionJSX", "pragmaFrag": "React.Fragment" }],
    ["emotion", { "autoLabel": true, "labelFormat": "[local]", "useBuiltIns": false, "throwIfNamespace": true, "sourceMap": true }]
  ]
}

tsconfig.json

{
  "compilerOptions: {
    ...
    "jsx": "preserve"
    ...
  }
}

Make sure you have installed the plugins required in .babelrc and you don't call React.createElement directly.

@karol-majewski Thanks it worked. But I tried using it with css prop babel preset as

{
  "presets": ["@emotion/babel-preset-css-prop"],
  "plugins": [
    ["transform-inline-environment-variables"],
    ["babel-plugin-jsx-pragmatic", { "export": "jsx", "module": "@emotion/core", "import": "___EmotionJSX" }],
    ["@babel/plugin-transform-react-jsx", { "pragma": "___EmotionJSX", "pragmaFrag": "React.Fragment" }],
    ["emotion", { "autoLabel": true, "labelFormat": "[local]", "useBuiltIns": false, "throwIfNamespace": true, "sourceMap": true }]
  ]
}

It doesn't work without me having to specify /** @jsx jsx */ on top of every file and doing import { jsx } from "@emotion/core"; Can't we make it work somehow by using @emotion/babel-preset-css-prop.

@paramsinghvc

I have an example repo that's using Babel Typescript, Parcel, Emotion 10 and the CSS prop (no pragma statements required) successfully here:
https://github.com/lee-reinhardt/parcel-ts-emotion

There is a summary of the workarounds with references in the readme.

@paramsinghvc You shouldn't combine @emotion/babel-preset-css-prop and babel-plugin-emotion. The former already uses the latter internally.

WTH ๐Ÿ˜คI've spent hours and this nasty [object Object] thing is not going away anyhow in my typescript react project.
Does anyone know a solution to this sad problem??

try this : https://github.com/LeetCode-OpenSource/emotion-ts-plugin/pull/26

For anyone still wanting to use Typescript + Emotion 10 without having to import /** @jsx jsx */ on every file, here's what I've done:

  1. In my webpack config, I added babel-loader to my tsx rule. I used this because I wanted the output of ts-loader to be handed over to babel so that the @emotion/babel-preset-css-prop can do its magic.
    ``` rules: [
    {
    test: /.tsx?$/,
    include,
    exclude,
    use: [
    {
    loader: 'babel-loader',
    options: { cacheDirectory: true },
    },
    {
    loader: 'ts-loader',
    options: {
    transpileOnly: true,
    },
    },
    ],
    },
    ],
Note: Webpack loaders are applied from right to left, that's why babel-loader is listed first

2. I added `@emotion/babel-preset-css-prop` as a  babel preset in my babel config.

3. Instead of having Typescript compile the JSX, I delegate that job to Babel. To do that, make sure you set `jsx: preserve` in tsconfig.json (https://www.typescriptlang.org/docs/handbook/jsx.html). Without this, babel won't be able to apply the `@emotion/babel-preset-css-prop` because when it receives the output from ts-loader, the JSX has already been transpiled.

4. In order to use the css prop on a jsx element with TS, Emotion augments the DOMAttributes interface of react with  the css prop in types. 

declare module 'react' {
interface DOMAttributes {
css?: InterpolationWithTheme
}
}

The problem is that typescript only sees the types defined in `@types/*` by default and emotion typings are in `@emotion/core`. To solve this, add `@emotion/core` to the `types` array in tsconfig.json or include the reference in a .d.ts file:

///
```

I elected to use the reference because when you set a types array in tsconfig, only the types listed are used. I rather keep the default of @types directory and add one-offs as needed.

That's all, hope it helps someone.

BTW, I think with this approach, you can continue to use the Fragment shorthand <>/ since Babel is transpiling the jsx

That's all, hope it helps someone.

Do whatever works for you - but this can hardly be called a minor change to existing setups. Using jsx pragma explicitly means that you don't have to do anything special to satisfy all the tooling that wants to understand JSX etc except adding a custom pragma because support for it is backed into nearly all the tooling. ๐Ÿคทโ€โ™‚

Do whatever works for you - but this can hardly be called a minor change to existing setups

@Andarist Correct me if I'm wrong, but one of the main use cases for the babel preset or the babel plugin is to avoid having to add the jsx import and the pragma at the top of each file where you want to use the css prop right? My comment simply shows a way that you can use the plugin with Typescript. That's all.

As to @emotion/babel-preset-css-prop - yes, but it has been created only because people were complaining about having to add pragma "manually" (where it can even be automated with eslint plugin, but with it it's still there - it's explicitly used).

As to babel-plugin-emotion (the primary one) - no. Its purpose is to generate source maps, labels, optimize few things and similar stuff. It doesn't let you skip writing pragma.

@Andarist Yeah, my bad. Just the preset. Which in my opinion is great and listed as the first option in the docs. It took me a while to figure why it wasn't working with Typescript, but like you said, it's the setup that works for me and may not be the best for everyone. I'm in the process of convincing my team to move from styled-components to emotion and I wanted it to be as simple as possible for them.

My apologies for replying to a closed ticket, it just happens to be the what popped up when I googled and I wanted to provide some info for others that may want to do something similar. Thanks for all the work on Emotion. It rocks!!!

No problem - I'm just always rather surprised how strongly people react to the whole custom pragma thing.

Your comment can be certainly helpful for others trying to do the same ๐Ÿ‘ I just don't understand why people want to go through such hoops ๐Ÿ˜… IMHO the ROI is just low for this one, but that might be just me.

any ideas about getting pretty class names (aka autoLabel) and source maps with pure typescript?

@jacek213 are you using webpack or just transpiling with tsc?

Any updates about this, so we don't need to use /** @jsx jsx */ and then we can use <></> for our components?

This is for now how i have it working with: Create React App (CRA) + TypeScript + Craco

Using "jsxFactory": "jsx" - you will need to add import { jsx } from '@emotion/core'; to evert components so thats redundant.

This is how it should look when using with <React.Fragment>

/** @jsx jsx */
import React from 'react'
import { css, jsx } from '@emotion/core'

const myTest = css`
  background-color: red;
  width: 400px;
  height: 400px;
  font-size: 10px;
`

function App(): JSX.Element {
  return (
    <React.Fragment>
      <div css={myTest}>Testing Emotion</div>
    </React.Fragment>
  )
}

export default App

And without <React.Fragment>

/** @jsx jsx */
// import React from 'react'
import { css, jsx } from '@emotion/core'

const myTest = css`
  background-color: red;
  width: 400px;
  height: 400px;
  font-size: 10px;
`

function App(): JSX.Element {
  return (
    // <React.Fragment>
    <div css={myTest}>Testing Emotion</div>
    // </React.Fragment>
  )
}

export default App

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "homepage": "./",
  "dependencies": {
    "@emotion/babel-preset-css-prop": "^10.0.27",
    "@emotion/core": "^10.0.28",
    "@types/react": "^16.9.34",
    "@types/react-dom": "^16.9.7",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.1",
    "typescript": "~3.8.3"
  },
  "devDependencies": {
    "@craco/craco": "^5.6.4",
    "@typescript-eslint/eslint-plugin": "^2.31.0",
    "@typescript-eslint/parser": "2.x",
    "eslint": "7.x",
    "eslint-config-prettier": "^6.11.0",
    "eslint-config-react-app": "^5.2.1",
    "eslint-plugin-flowtype": "4.x",
    "eslint-plugin-import": "2.x",
    "eslint-plugin-jsx-a11y": "6.x",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-react": "7.x",
    "eslint-plugin-react-hooks": "4.x",
    "prettier": "^2.0.5"
  },
  "scripts": {
    "dev": "craco start --config config/webpack.dev.js",
    "build": "craco build --config config/webpack.prod.js",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

@MariuzM Hi, you can try this plugin: https://github.com/LeetCode-OpenSource/emotion-ts-plugin
I have made a pull request to auto inject the /** @jsx jsx */ behind each React import expression.
Hope this will help you!

So we have two direction to solve this problem:

  • emotion-ts-plugin
  • babel

For me emotion-ts-plugin was 3x times faster, but i spend much time to found how to declare jsx auto injected to files.
Solution was:

import {createElement} from "@types/react";
declare global {   
    var jsx: typeof createElement = createElement; 
}

through tsconfig.json option "types": ["./typings/index"], /* Type declaration files to be included in compilation. */

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AlexanderProd picture AlexanderProd  ยท  3Comments

mattfysh picture mattfysh  ยท  3Comments

meebix picture meebix  ยท  3Comments

pimmey picture pimmey  ยท  3Comments

mitchellhamilton picture mitchellhamilton  ยท  3Comments