Making Emotion React work with Vite

According to Emotion docs, the standard way to use Emotion is with css prop of jsx components. For first time users, it's easy to miss out on the required pragma header and just add the css prop directly. In that case, this is what you will get:

<div css="You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).">
</div>

To fix that, remember to add a jsx pragma at the top of file /** @jsx jsx */. But If your project is configured via Vite, you are to get the following error:

💡
pragma and pragmaFrag cannot be set when runtime is automatic

Turn out Emotion documentation does mention that:

If you are using a zero-config tool with automatic detection of which runtime (classic vs. automatic) should be used and you are already using a React version that has the new JSX runtimes (hence runtime: 'automatic' being configured automatically for you) such as Create React App 4 then /** @jsx jsx / pragma might not work and you should use /* @jsxImportSource @emotion/react */ instead.

Use jsxImportSource Pragma

But following the instruction and replace @jsx with @jsxImportSource results in another error thrown by Vite:

💡
The JSX import source cannot be set without also enabling React's "automatic" JSX transform

According to this typescript document, jsxImportSource pragma specifies which factory method should be used to compile jsx string into plain old javascript objects. Look like Vite needs to know that it can automatically determine which jsx transform method to use on a per file basis. In order for the css prop mentioned above to function properly, Vite should use Emotion provided jsx transform function instead of React's default (i.e React.createElement)

Update Vite.config.js

A little digging around the web, it's possible to configure jsxImportSource via the vite.config.js file instead. Add the bellow lines to the Vite config file:

plugins: [react({
    jsxImportSource: "@emotion/react",
    babel: {
    	plugins: ["@emotion/babel-plugin"],
    },
  })
]

That let Vite knows definitely that it should use jsx transform function from Emotion for the whole project. It has the added benefit of not having to add pragma comment at the top of every source file.

And be sure to install the Emotion Babel plugin

npm install -D @emotion/babel-plugin

Now that the configuration is updated, Emotion based JSX components are transformed properly in a Vite project.