19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Next.js & StorybookにEmotionが効かない問題の解決メモ

Last updated at Posted at 2022-08-15

TL;DR

  • Next.js⇄Storybook間の設定共有は storybook-addon-next がほぼ全部解決してくれる。まずはこれを使え。
  • ただし Emotion などのJSXを拡張するライブラリを使う場合、アドオンが自動で解決してくれない。Storybook側の設定でも記述する必要がある
  • Storybookの設定でドキュメント構文である MDX をv1→v2に上げないと Emotion を入れたときに落ちる

経緯

個人開発でフロントエンドを作る必要があったので、
簡単にサクッと作りつつ、かつある程度はしっかりした構成にしよう、
ということで以下のような形にセットアップすることを目標に作業を始めた。

  • React (フロントフレームワーク)
  • TypeScript (言語)
  • Next.js (ディレクトリ構成とオートルーティングが使いたかったので)
  • Storybook (コンポーネント単位の実装と確認を楽にする)
  • Emotion (描きやすいCSSinJS)

Next.js と Storybook の導入まで

とりあえずcreateコマンドを使ってNextの雛形をTypeScript構成で作成。対話形式で色々聞いてくるので適当に入力する。

$ yarn create next-app --typescript

次にStorybookを導入する。こっちもCLI形式で導入するのでよしなに選択。

$ npx storybook init

で、ここにNext.js側の設定(画像インポートの取り扱いやページルーティングの挙動など)をStorybookに展開してくれるアドオン storybook-addon-next を導入する。

$ yarn add -D storybook-addon-next
.storybook/main.js
module.exports = {
  ...
  addons: [
    'storybook-addon-next',         // ← インストールしたNext.js連携用アドオンを追加
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions'
  ]
  ...
}

これでNext.jsとStorybookのセットアップは完了。
ほんと最近の環境構築は楽である。ノーコンフィグ・ゼロコンフィグ思想万歳!

Emotionの導入

JSXの基本的な機能(要素のprops)を拡張するためここはちょっと手間。
まずはパッケージを入れる。

$ yarn add @emotion/react
$ yarn add -D @emotion/babel-plugin

次にプロジェクトルートにBabelの設定ファイルを作成。個人的な好みで babel.config.js とした。
ここにNextの標準設定をインポートし、その上でEmotionの拡張処理が注入されるように設定する。

babel.config.js
module.exports = {
  presets: [
    presets: [
      [
        'next/babel',
        {
          'preset-react': {
            runtime: 'automatic',
            importSource: '@emotion/react'
          }
        }
      ]
    ],
    plugins: ['@emotion']
  ]
}

またTypeScript側でもエラーが出ないよう、Emotionを注入する形に設定を書き換える。

tsconfig.json
{
  ...
  "types": ["@emotion/react/types/css-prop"],
  "jsxImportSource": "@emotion/react"
  ...
}

typesjsxImportSource はもしかしたらどっちかでよかったかもしれない。
がまあめんどいのでどっちも入れておくことに。検証は後回し。

これでNext側では見事にEmotionによるスタイリングが効くようになった。
しかしStorybookで起動してみるとスタイルをあてたはずのCSS要素に以下のような文面が。

<span 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).">

なんか効いてないっぽいメッセージが出てるわね

対策1.Storybook側にBabelの設定を記述

Storybookの設定ファイルである main.js から強引にログを吐き出させてみると、
どうやらNext側のBabel設定が一部しか読み込まれておらず、Storybook側にEmotionの注入設定が渡っていないことがわかった。

ならばStorybookでも個別に設定してやるまで。

.storybook/main.js
module.exports = {
  ...
  babel: async (options) => {
    // emotion用の注入設定
    // preset-react ローダーを取得してくる
    const presetReact = options.presets.find((p) => /preset-react/.test(p[0]));
    // preset-react ローダのオプションを設定
    presetReact[1] = {
      ...presetReact[1],
      runtime: 'automatic',
      importSource: '@emotion/react'
    }
    // Emotionプラグインを追加
    options.plugins.push(require.resolve('@emotion/babel-plugin'));
    
    return options;
  }
}

とりあえずこれで理屈上は動くはずなので、Storybookを起動してみる。
ところが今度は以下のようなエラーが発生して落ちてしまった。

src/stories/Introduction.stories.mdx: importSource cannot be set when runtime is classic.

通常のコンポーネントに対するビルドは通っているようで、ドキュメントファイルである .mdx を削除すれば正常に起動した。

対策2. MDXのバージョンを上げる

とはいえドキュメントファイルだけ落ちるのも気持ち悪いので原因を探してみることに。
preset-react ライブラリにおいて、 runtime: classic はEmotionなどの外部ライブラリの注入に対応していないため automatic に設定する必要がある(Babel公式Docs参照)。
しかし、そもそもローダー側では runtime をしっかり automatic に設定しているのに、 classic として扱われているのが妙にひっかかる。

ざっと調べてみたら以下のissueにたどり着いた。
https://github.com/mdx-js/mdx/issues/1441

どうやらMDXのビルドプラグインが runtime: classic のみに対応していたようで、
最近出た v2 以降だと runtime: automatic に対応するようになったようだ。
Storybook標準で採用しているMDXのビルドプラグインは v1.6.22 となっており、これを上げれば解決しそう。

調べたらまだ実験的昨日の段階だが、MDX2を有効にする機能がStorybook内にあるらしい
これを使ってバージョンを上げてみる。

$ yarn add -D @storybook/mdx2-csf
.storybook/main.js
module.exports = {
  ...
  features: [
    previewMdx2: true
  ]
  ...
}

これでStorybookを起動してみる…よし、動いた! スタイリングもちゃんと反映されている!

サクッとセットアップを済ませるはずだったが予想外に手間取ってしまった。
やはりノーコンフィグといえども新しめのライブラリへの対応は限界があるわけで、
その辺りはちゃんとやらないと動かないんだな…と痛感。
妙に疲れたので実装は明日にしよう…

19
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?