【Next.js & TypeScript】Emotionの導入が大変だったので手順をまとめておく
CSS in JSとして後発だけありEmotionは使い勝手いいですよね。Material UIがv5からEmotionを利用している影響もあるのか、React版の@emotion/reactはnpmダウンロード数の伸びがすごいです。

@emotion/css vs @emotion/react vs @emotion/styled vs styled-components
(赤が Styled Component、他が Emotion の主要ライブラリ)
ただ、EmotionをNext.js & TypeScriptに導入するのはそこそこ面倒です。ハマりどころも結構多いので手順をこの記事にまとめておきます。Next.js & TypeScriptでEmotionを使おうと思っている方の参考になれば嬉しいです。
筆者の環境
- Next.js v11.1.2
 - React v17.0.2
 - Emotion v11.4.1
- @emotion/react を使用
 
 - TypeScript v4.4.3
 
Emotion 導入済みのソースコード
この記事の設定をすべて済ませたコードをGithubに上げています。
※Next.js公式にもEmotionの例があるのですが、そちらはTypeScriptではないので設定が違います。
EmotionをNext.js & TypeScriptに導入する手順
それでは詳しい手順を見ていきましょう。
まず、Emotionのパッケージと、Emotionのbabel用プラグインを入れます。
yarn add @emotion/react @emotion/babel-plugin
そしてEmotionを読み込んで使うわけですが、そのまま下記のようにしても利用できません。
import { css } from "@emotion/react"
これはEmotionが以下のようにタグやコンポーネントにcss propを使うため、JSX→JavaScriptのコンパイル設定を調整する必要があるからです。
<div css={{ marginTop: "10px" }}>
  <p>テキスト</p>
</div>
JSXのコンパイルを調整するには2つの方法があります。
- JSXプラグマと言われるディレクティブを書く方法
 - BabelのConfigを設定する方法(おすすめ)
 
以下で詳しく見ていきましょう。
JSXプラグマを使う方法
JSXプラグマというディレクティブを追記する方法です。具体的には下記のようなプラグマ(/** 〜〜 */)をモジュール読み込みの前に追加します。
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react"
この方法はこれだけでオッケーなので簡単なのですが、すべてのコンポーネントファイルに対してこの記述をするのは冗長な感じがしますよね。
そこでおすすめなのがBabelのConfigを設定する方法です。babelのconfigファイルを追加する必要はありますが、一度設定すればプラグマの記述は必要なくなります。
BabelのConfigを設定する方法(おすすめ)
手順ですが、Next.jsのトップディレクトリで .babelrcを作成します。
vi .babelrc
そして下記内容で保存します。
{
  "presets": [
    [
      "next/babel",
      {
        "preset-react": {
          "runtime": "automatic",
          "importSource": "@emotion/react"
        }
      }
    ]
  ],
  "plugins": ["@emotion/babel-plugin"]
}
これだけです。簡単ですね。
ちなみに、以前はこの方法を使うと以下のようなReactのshorthand syntax(<> </>)が使えなくなる問題が発生していたのですが、今は解消されておりました。
return (
   <>
       <Hoge />
   </>
)
TypeScript の型定義ファイルを読み込む
TypeScript用にEmotionの型定義ファイルが必要です。
Emotionの型定義ファイルは @emotion/react/types/css-propにあるのですが、tsconfig.jsonを使って読み込むのがいいでしょう。
tsconfig.jsonのcompilerOptionsにtypesを追加して Emotion の型定義ファイルを指定します。
{
  "compilerOptions": {
    〜
    "types": ["@emotion/react/types/css-prop"],
    〜
  }
  〜
}
ちなみにネット上ではnext-env.d.tsに以下を追加する方法も紹介されてたりしますが、筆者の環境だとビルドの際にnext-env.d.tsはリセットされるのでこの方法は使えませんでした。
/// <reference types="@emotion/react/types/css-prop" />
ただ、additional.d.tsなど別ファイルを作ってそちらに上記を記述し、tsconfig.jsonのincludeでadditional.d.tsを追加するというのも可能ではあると思います。
以上でEmotionがNext.js & TypeScriptで動作するようになったと思います。
しかしEmotionを導入したことでいくつか注意点があるので確認しておきましょう。
Emotion導入後の細かい注意点
Linkコンポーネントが正しく動作しなくなる
Emotionはcss propを使うためにJSXのコンパイル設定をいじっていることをすでに書きました。
その副作用だと思うのですが、Next.js標準のLinkコンポーネントにおいて、aタグにcss propがある場合、aタグのhref属性が自動生成されない問題が発生します。
なので、下記のように Linkコンポーネントに毎回passHrefを追加するか、
<Link href="/hoge" passHref>
  <a css={foo}>リンクボタン</a>
</Link>
下記のようにLinkコンポーネントをラップしたコンポーネントを用意する必要があります。私はこちらの方法で対応しています。
import Link from "next/link"
type Props = {
  children: React.ReactNode
  href: string | URL
  as?: string
}
export default function MyLink(props: Props) {
  return (
    <Link href={props.href} as={props.as} passHref>
      {props.children}
    </Link>
  )
}
SSR するなら@emotion/css は非推奨
Emotionの主要なライブラリは@emotion/css、@emotion/react、@emotion/styledがありますが、SSRをする場合は@emotion/cssは非推奨となっています。
@emotion/cssはcss propを使わず、ReactのclassNameを使う方法です。css propを使わないのでJSXコンパイルの設定変更が不要というメリットはあるのですが、SSRにおいてpre-rendering時にCSSが読み込まれなくなる現象が発生します。
なので、SSRを使う場合は@emotion/react、@emotion/styledのどちらかを使用することになります。
シンタックスハイライト、入力補完は Styled Component のものを入れる
コードのハイライトや入力補完はEmotion専用のものはほとんどないと思います。
ただ、Styled Componentのものを利用すれば問題なく対応できる場合が多いと思います。
VSCodeであればvscode-styled-componentsを使えばいいでしょう。tsxファイル内でも以下のように見やすく表示されますし補完もしてくれます。

まとめ
Emotionは実際に使ってみるととても便利なのですが導入時にハマりどころが多いです。
これもcss propというEmotionの独自機能が原因なのですが、この機能自体がEmotionが便利な理由でもあるのでしょうがない面もあるかなと思います。
Emotionの初期設定を済ませたコードはGithubに置いてあります。それではみなさんも良いEmotionライフをお過ごしください。
Discussion