Next.jsにおけるCSSの取り扱い方法 2020年9月版

公開:2020/09/26
更新:2020/09/28
8 min読了の目安(約5200字TECH技術記事

はじめに

CSSとしたがNext.jsにおけるスタイルの取り扱い方について。
公式ドキュメント見れば基本的なことはわかるのだがIssuesやDiscussionsを追わないと分かりにくいことも多いのでまとめる。

2020年9月版としたのはすぐにでも動きがありそうなため。将来的なこともなるべく記載する。

Build-in CSS Support

Basic Features: Built-in CSS Support | Next.js

デフォルトでCSSのサポートがされている。かつては @zeit/next-css を導入する必要があったが今は必要ない。

Sassについても

Before you can use Next.js' built-in Sass support, be sure to install sass

とパッケージを追加する必要はあるがbuilt-inで対応済み。

Adding a Global Stylesheet

pages/_app.jsでGlobal Stylesheet Importが可能である。以下は公式ドキュメントの例。

/* styles.css */
body {
  font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica',
    'Arial', sans-serif;
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}
// pages/_app.js
import '../styles.css'

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

_app.jsはどのページでも必ず実行されるため注意が必要。Global Stylesheetが巨大化すればその分だけ初期ロードは重たくなる。

公式ドキュメントでは

For global stylesheets, like bootstrap or nprogress, you should import the file inside pages/_app.js.

bootstrapnprogressをGlobal Stylesheetの例としている。
他にはNormalize.cssなどGlobal reset系などは_app.jsで読み込むのが良い。

Importing CSS from Third Party React Components

上記のbootstrapnprogressのようにnode_modulesから読み込むことはpages/_app.jsでしか許可されてこなかった。

next.js/css-global.md at v9.5.3

Why This Error Occurred
An attempt to import Global CSS from a file other than pages/_app.js was made. Global CSS cannot be used in files other than your Custom <App> due to its side-effects and ordering problems.

これに関してVercelのMaintainerからRFCが出された
RFC: Importing CSS from Third Party React Components

これまでの制約はとても不便だった。
例えばアプリケーションの一部で@reach/sliderを使うと仮定する。公式の例では

import {
  Slider,
  SliderInput,
  SliderTrack,
  SliderTrackHighlight,
  SliderHandle,
  SliderMarker,
} from "@reach/slider";
import "@reach/slider/styles.css";

と使用するコンポーネントとCSSを読み込むようにドキュメントに記述されている。

しかしNext.jsで使用する場合にはCSSは必ずpages/_app.jsでimportする必要があった。
@reach/sliderはアプリケーションの一部でしか使われていない。それにも関わらずCSSはGlobal importされるため無駄が多かった。

このRFCは上記の問題を解決するものだ。まだ提案段階ではあるが以下のような記述をする。

import { Slider } from "@reach/slider";
import { className } from "@reach/slider/styles.css";

function Example1() {
  return (
    <div className={className}>
      <Slider min={0} max={200} step={10} />
    </div>
  );
}

function Example2() {
  return <Slider className={className} min={0} max={200} step={10} />;
}

To use the vendor CSS, include the class name somewhere in the parent DOM:

このRFCの一環としてImporting CSS from Third Party React Components #16993 がマージされた。v9.5.4-canary.10以降で試すことができる。

これはRFC通りの実装はまだ達成されておらず、

// components/MySlider.tsx
import { Slider } from "@reach/slider";
import "@reach/slider/styles.css";

function Example() {
  return <Slider min={0} max={200} step={10} />;
}

This pull requests adds the ability to import Global CSS anywhere in your application

と、Global CSSをアプリケーションのどこでもimportが可能になったに過ぎないので注意が必要である。
(CSS形式で配布されている必要はあるにせよ)これから3rd party UI LibraryをNext.jsで利用しやすくなりそうである。

個人的には
Allow importing CSS modules from other NPM packages
の解決にも期待している。

Recommend CSS Modules over css-in-js

まだIssueに上げられている段階でDocsには反映されていないがcss-in-jsよりもCSS Modulesを推奨するようだ。
[Docs] Recommend CSS Modules over css-in-js

Next.jsを開発するVercelはstyled-jsxというcss-in-jsライブラリを管理しており、built-inでサポートしている。

公式の例である通り

<style jsx>{`
  …
`}</style>

とするだけで良い。このような記載もありユーザーがどちらを使えば良いのか混乱するために上記のissueが上がっているのだろう。
他にもstyled-componentsなどcss-in-jsライブラリは存在するが設定が必要でNext.jsの自動最適化が効きにくくなることが予測される。

以上のことから個人的にはCSS Modulesを利用するようにしている(CSS Modules自体の将来性への懸念はあるが)。

Tailwind CSS

Tailwind CSS は最近人気のあるA utility-first CSS frameworkだ。

@leeerob 氏(現Vercel社 Solutions Architect)
の「Next.jsとTailwindの相性最高だぜ!(筆者意訳)」という旨のツイートに対して
@timneutkens 氏(現Vercel社 Next.js lead engineer)が以下のようにリプライをしている。

Tailwind CSS の作者である@adamwathan氏と協議を重ねているようだ。

Next.jsのExampleにもTailwind CSSの活用例は存在しており今でも使うのにそこまでの苦労はない。
特にRFCも出ていないので詳細は何もわからないがビルドサイズの最適化などNext.jsとの親和性は高くなりそう。個人プロジェクトはTailwind CSSで済ませることも多いので期待。

ちなみに公式サイトは最近になってNext.js+Vercelでリニューアルされている。

まとめ

個人的ベストプラクティスは

  • Global Style Sheet は最低限にして pages/_app.js でimportする。
  • 各コンポーネントはCSS Modulesでスタイルを書く。
  • css-in-jsは使わない。
  • Tailwind CSS との相性はよくなりそうなのでこのまま使う。