LoginSignup
47
33

More than 1 year has passed since last update.

Next.js 10 + TypeScriptでEmotionを使う

Last updated at Posted at 2020-12-25

はじめに

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

次に、babelpresetsにこれを使うように記述して、tsconfig.jsoncss propの型がちゃんと設定されるように記述します。

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

tsconfig.json
{
  "compilerOptions": {
    // ...
    "jsxImportSource": "@emotion/react"
  }
}

これでEmotionを使う準備ができました。
試してみましょう。

Hello.tsx
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>
  )
}

スクリーンショット 2020-12-25 16.37.33.png

ただし、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を修正します。

_document.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>
    )
  }
}

_app.tsx
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などのセレクタも使えるようになりました。
コンポーネントを修正して試してみましょう。

Hello.tsx
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>
  )
}

スクリーンショット 2020-12-25 16.36.14.png

まとめ

最後まで読んでいただきありがとうございました!

EmotionはReactで使えるCSS in JSライブラリの中で一番気に入っているライブラリなので、是非皆さんに使っていただきたいなーと思います!

参考

47
33
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
47
33