Next.js と Emotion でいろいろハマった件 1

個人的に Emotion が好きなのでよく使っていますが、今回初めて Next.js のプロジェクト内で使ってみて色々ハマったのでまとめてメモしておきます。
JSX Pragma をつけずに使うにはどうするかと、それに伴う Fragment のインポートや tsc エラーについて書き留めました。

Version

  • TypeScript 4.0.5
  • Next.js 10.0.1
  • React 17.0.1
  • Emotion 10/11

Next.js について

React のフレームワークです。

Emotion について

個人的所感。

Emotion :CSS in JS。

以下の 3 つから選んで好きなのを使う。

  • @emotion/core React で Emotion を使うときにはこれを選ぶ
  • @emotion/styled Styled-Component を使うときに選ぶ
  • emotion Vanilla Emotion とも。React を使わないときにはこれを使う

これまでは React 以外のプロジェクトで使うことが多かったので emotion を使っていました。 そして過去に 1 件制作した Next.js のプロジェクトでも emotion を使って書いていました。

今回も Next.js のプロジェクトだったので emotion でいいかなと思ったのですが、<Global /> でグローバルスタイルが書ける…とのことで @emotion/core を使ってみました。

導入早々出会った問題たち

/** @jsx jsx */ プラグマ問題

これは css props を使用しているすべてのファイルの先頭に書かなくてはいけません。これを書かずに <ComponentName css={...}> などしてしまうと出力結果の class に長々とエラーが出力されます。

でも全ファイルなんて絶対書きたくない…と思って色々調べた結果、以下の方法で書かなくてもよくなりました。

Emotion 10 の場合

  1. babel-plugin-emotion@emotion/babel-preset-css-prop をインストールする

    npm install @emotion/babel-preset-css-prop
  2. プロジェクト root に以下の .babelrc を設置する

    {
      "presets": [
        "next/babel",
        "@emotion/babel-preset-css-prop"
      ]
    }

Next.js のプロジェクトだと .babelrcpresets: []"next/babel" がないとビルドに失敗します。 Emotionの公式サイトにありますが読み込み順を指定しないと行けないようです。

このプリセットと .babelrc の追加で /** @jsx jsx */ とか import { jsx, css } from '@emotion/core'; とかやらなくて良くなりました。

Emotion 11 の場合

Emotion 11 ではパッケージ名が変わりました。バージョンをあげる場合にはリネーム作業が必要です。

  1. @emotion/babel-plugin をインストールする

    npm install @emotion/babel-plugin
  2. 公式にしたがって .babelrc を作る

    {
      "presets": [
        [
          "next/babel",
          {
            "preset-react": {
              "runtime": "automatic",
              "importSource": "@emotion/react"
            }
          }
        ]
      ],
      "plugins": ["@emotion/babel-plugin"]
    }

<></> 使えない問題

Emotion で <></> を使うと "ReferenceError: React is not defined" が出力されます。

これは公式にも書いてありました。

As a result you may be not able to use react fragment shorthand syntax - <></>, but still you can use <Fragment></Fragment>.

Emotion - TypeScript

<></> を使うのはすべて <React.Fragment></React.Fragment> で置換するしかなさそうです。

GitHub の issue を読んでいると window.React = React; のように import したモジュールを Global に代入して凌いだ方もいるようです。 ただ、Global をいじるくらいなら諦めて <React.Fragment></React.Fragment> を使ったほうがいいと思います。

ts(2686) がでる

<React.Fragment></React.Fragment> を使おうとすると ts から以下のエラーが得られます。

'React' は UMD グローバルを参照していますが、現在のファイルはモジュールです。代わりにインポートを追加することを考慮してください。 ts(2686)

これは @types/react の記述によるものです。

import React from 'react';

とすればこのエラーは表示されなくなります。 しかしこいつ、VS Code のコード補完では自動で import を追加してくれません。ctrl+. のクイックフィックスを動かす必要があります。

これは React.Fragment ではなく Fragment のみでコンポーネントを書くとクイックフィックスでなくても自動でインポートしてくれるので、<Fragment></Fragment> を使ったほうが良さそうです。

まとめ

ハマり過ぎて数時間使ってしまった諸々でした。 babel や webpack についてちゃんと勉強しないといけませんね。

ちなみに Next.js は何も設定しなくても CSS Modules や CSS-in-JS が使えるので、記法にこだわりが無ければそっちを使ったほうが全然楽そうでした。

関連記事
unimoku

Web サイトの制作、運営とアプリケーションのフロントエンド開発などをやっています。主に使うのはTypeScript、JavaScript、PHP、C/C++。特にTypeScriptが好きです。