React.jsでemotionを使うときはJSX Pragmaをつける必要がありまして。


普通にimportしただけだと動かない

EmotionというCSSフレームワークを使ってちょっとお試しで実装していました。

package.jsonは以下の通りとなっております。

{
  "dependencies": {
    "@emotion/core": "^10.0.28",
    "next": "9.4.4",
    "react": "16.13.1",
    "react-dom": "16.13.1"
  },
  "devDependencies": {
    "@types/node": "^14.0.22",
    "@types/react": "^16.9.43",
    "typescript": "^3.9.6"
  }
}

スタイルを設定したいページにEmotionをimportしたのですが・・・。

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

これだけだとスタイルが適応されないので調査いたしました。
どうやら下記のように@jsx jsxがないとうまいことコンパイルされないようでした。

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

jsxをコンパイルする際は、Emotionは独自のコンパイラを通す必要があり、/** @jsx jsx */というJSX Pragmaの記述をしないと動きませんでした。

以下、名著「りあクト! TypeScriptで極める現場のReact開発」から引用

「Emotion はちょっと他のライブラリと違って特殊なことをしているので、使うにあたっていくつか注意事項があるのね。Emotion の文法を使って記述したJSXは通常React.createElement() ではなくEmotion が提供するコンパイラを通す必要があるの。」
「@emotion/core からインポートしてるjsx というのがそのコンパイラね。で、JSX リテラルで書いたコードをそれに渡すためにファイル冒頭で/** @jsx jsx */ の『JSX Pragma』というマジックワードを記述してあげてるの」
「えっ、言われるまで気づきませんでした。そうなんですね」
「さらに、これに起因する注意点が三つあるの。ひとつめは『親コンポーネントでJSX Pragma を使用したら、子のコンポーネントでもJSX Pragma を使わなければならない』こと」
「使わないとどうなるんですか?」
「『ReferenceError: React is not defined』って怒られてコンパイルに失敗するね。場合によっては直上の親コンポーネントにもJSX Pragma が必要なときもある。Presentational Component をインクルードしてContainer Component を作るときとか、それ自体がEmotion を使ってなくてもJSX Pragma を書かないとコンパイルが通らない」

コンパイルエラーとありますが、私の環境ではエラーは出ずに、スタイルが当たらないという状況でした。

実装例

Emotionでは子コンポーネントでもJSX Pragmaの記述が必要ということで、
お試しとして、Indexからヘッダーを呼び出すものを実装してみました。

↓こんな感じです。

src
│
└─pages
    index.tsx
    header.tsx
header.tsx
/** @jsx jsx */
import { Global, css, jsx } from "@emotion/core";

const globalCss = css`
  * {
    margin: 0;
    padding: 0;
  }
`;

const styles = {
  titleBase: css`
    height: 168px;
    width: 100%;
  `,
  title: css`
    height: 168px;
    width: 100%;
    font-family: Roboto;
    font-size: 64px;
    color: #ffffff;
    background: #2f80ed;
  `,
};

interface Props {
  title: string;
}

const Header = (props: Props) => {
  return (
    <>
      <Global styles={globalCss} />
      <div css={styles.titleBase}>
        <h1 css={styles.title}>{props.title}</h1>
      </div>
    </>
  );
};

export default Header;
index.tsx
import Header from "./header";
/** @jsx jsx */
import { css, jsx } from "@emotion/core";

const styles = {
  content: css`
    height: 100%;
    width: 100%;
    font-size: 64px;
  `,
};

export default function Index() {
  return (
    <>
      <Header title="タイトル" />
      <div css={styles.content}>Content</div>
    </>
  );
}

起動させると、こんな感じで表示されます。

Emotionをjsxで使いたい場合、JSX Pragmaを忘れずにつける必要がありました。気を付けたいですね