はじめに
Next.js 10になって、React 17になったりしてEmotionが動くようにするのに少し工夫が必要になりました。
その方法について、まだEmotionの公式のドキュメントではまとめられていないようだったので、試行錯誤した内容をここに記事として残そうと思います。
この記事で作り上げた環境はこちらに用意してあります。
リポジトリ: https://github.com/282Haniwa/next-with-emotion
Next.jsのプロジェクトを作る
Next.jsの環境構築はこちらの記事で解説しているので、省略させていただきます。
とりあえず動く環境で試したい方は、
$ git clone https://github.com/282Haniwa/next-example.git
$ cd next-example
$ yarn install
$ yarn dev
で環境を作ってください。
Emotionを導入する
Emotionを動かすのに必要なパッケージを追加します。
$ yarn add @emotion/react
これで、コンポーネントを以下のように編集すると、Emotionを使うことができます。
/** @jsxImportSource @emotion/react */
import React from 'react'
import { css } from '@emotion/react'
const hello = css`
color: red;
`
type Props = {
name: string
}
export const Hello: React.FC<Props> = (props) => {
const { name } = props
return (
<div>
<h1 css={hello}>Hello {name}!</h1>
</div>
)
}
公式では、
// this comment tells babel to convert jsx to calls to a function called jsx instead of React.createElement
/** @jsx jsx */
import { css, jsx } from '@emotion/react'
のように/** @jsx jsx */
と記述するようにとありますが、React 17からは
pragma and pragmaFrag cannot be set when runtime is automatic.
というようなエラーが出るようになっているので、この設定を無効化するように設定するか、/** @jsxImportSource @emotion/react */
というようなプラグマにする必要があります。
全てのコンポーネントに/** @jsxImportSource @emotion/react */
というjsxプラグマを記述することでEmotionを動作させることは可能ですが、毎回書くのはめんどくさいので@emotion/babel-preset-css-prop
を導入して書かなくてよくしてしまいます。
まず、@emotion/babel-preset-css-prop
を入れます。
$ yarn add --dev @emotion/babel-preset-css-prop
次に、babel
のpresets
にこれを使うように記述して、tsconfig.json
にcss
propの型がちゃんと設定されるように記述します。
{
"presets": ["next/babel", "@emotion/babel-preset-css-prop"]
}
{
"compilerOptions": {
// ...
"jsxImportSource": "@emotion/react"
}
}
これでEmotionを使う準備ができました。
試してみましょう。
import React from 'react'
import { css } from '@emotion/react'
const hello = css`
color: red;
`
type Props = {
name: string
}
export const Hello: React.FC<Props> = (props) => {
const { name } = props
return (
<div>
<h1 css={hello}>Hello {name}!</h1>
</div>
)
}
ただし、Emotionで:nth-child()
など、一部のセレクタを使いたい場合にSSRだとうまく機能しない場合があります。
nth-child
などを使わない場合は、以下の設定をしないことを推奨されているので、必要ない場合はここまでの設定で大丈夫ですが、必要になる場合は次の設定を行ってください。
SSRでnth-child
などのセレクタを使えるようにする(オプション)
完成形: https://github.com/282Haniwa/next-with-emotion/tree/with-nth-child
cloneするにはブランチを指定してください。
$ git clone https://github.com/282Haniwa/next-with-emotion.git -b with-nth-child
まず、必要なパッケージを追加します。
$ yarn add @emotion/server @emotion/css
次に、_document.tsx
と_app.tsx
を修正します。
import React from 'react'
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
DocumentInitialProps,
} from 'next/document'
import { extractCritical } from '@emotion/server'
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext,
): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx)
const styles = extractCritical(initialProps.html)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(' ')}
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</>
),
}
}
render(): JSX.Element {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
import React from 'react'
import { AppProps } from 'next/app'
import { CacheProvider } from '@emotion/react'
import { cache } from '@emotion/css'
import 'src/styles/globals.css'
const MyApp: React.FC<AppProps> = ({ Component, pageProps }) => {
return (
<CacheProvider value={cache}>
<Component {...pageProps} />
</CacheProvider>
)
}
export default MyApp
これで、nth-child
などのセレクタも使えるようになりました。
コンポーネントを修正して試してみましょう。
import React from 'react'
import { css } from '@emotion/react'
const hello = css`
color: red;
&:nth-child(2n) {
color: blue;
}
`
type Props = {
name: string
}
export const Hello: React.FC<Props> = (props) => {
const { name } = props
return (
<div>
<h1 css={hello}>Hello {name}!</h1>
<h1 css={hello}>Hello {name}!</h1>
</div>
)
}
まとめ
最後まで読んでいただきありがとうございました!
EmotionはReactで使えるCSS in JSライブラリの中で一番気に入っているライブラリなので、是非皆さんに使っていただきたいなーと思います!