Help us understand the problem. What is going on with this article?

Next.jsとCSS

More than 1 year has passed since last update.

ReactでCSSを書くにはたくさんの方法があると思います。
その中でもNext.jsでそれらを使用するにはどうしたらよいかを一覧で書いてみました。
それぞれのフレームワークの記法にはあまり触れずに、Next.jsをプロダクションレディーにするための設定とGlobalなCSSの書き方に触れます。

CSS not in JS

Sass with CSS modules

Next.jsのconfigに以下を追加

next.config.js
const withSass = require('@zeit/next-sass')
module.exports = withSass({
  cssModules: true
})

Sassファイルのstyles.scssを作成

styles.scss
$font-size: 50px;
.example {
  font-size: $font-size;
}

コンポーネント内でclassNameとしてオブジェクトを引き出します

pages/index.js
import css from "../styles.scss"

export default () => <div className={css.example}>Hello World!</div>

これでスコープドなCSSが書けます。

Globalなスタイル

Next.jsではクライアントの全ページで使用されるのはpages/_app.jsなので、こちらでimportすればOKです。pages/_document.jsでのimportは効きません。

pages/_app.js
import "../styles/global.scss";

また、直書きで良いのであれば、 pages/_document.jsにテンプレートリテラルで書く方法もあります。

/pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <html>
        <Head>
          {/* このようにテンプレートリテラルで直書きする */}
          <style>{`body { margin: 0 } /* custom! */`}</style>
        </Head>
        <body className="custom_class">
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}

https://github.com/zeit/next.js/wiki/Global-styles-and-layouts

CSS in JS

styled-jsx

Next.jsを作ったZeitがstyled-jsxもOSSとして作っています。
なのでNext.jsではデフォルトでstyled-jsxがデフォルトでロードされていて、no configで実装が開始できます。

pages/index.js
export default () => (
  <div>
    Welcome to next.js!
    <style jsx>{`
      div {
        background: red;
      }
    `}</style>
  </div>
);

詳しい記法などはこちらを参照して下さい。

Globalなスタイル

styled-jsx記法ではglobalというパラメータをつけると、書いたクラス名やタグがグローバルなものとして出力されます。これも同じく_app.js内で記載するとよいでしょう。

pages/_app.js
import React from "react";
import App, { Container } from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;

    return (
      <Container>
        <Component {...pageProps} />
        <style jsx global>{`
          body {
            background-color: green;
          }
        `}</style>
      </Container>
    );
  }
}

styled-components

shell
npm i --save styled-components
npm i -D babel-plugin-styled-components
.babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    "styled-components"
  ]
}

styled-componentsをNext.jsで使用するためには、実はこれだけでは足りません。サーバーサイドレンダリングされる際にstyled-compoentnsが実行されないため、クライアントでReactが実行されてからスタイルが当たるようになっています。ですから、<head>タグにスタイルを入れるためにServerStyleSheet_document.jsで実行しましょう。こちらに例が載っています

pages/_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps (ctx) {
    const sheet = new ServerStyleSheet()

    const originalRenderPage = ctx.renderPage
    ctx.renderPage = () => originalRenderPage({
      enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
    })

    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps, styles: [...initialProps.styles, ...sheet.getStyleElement()] }
  }
}

以上でstyled-componentsでの開発ができるようになります。

pages/index.js
import React from 'react'
import styled from 'styled-components'

const Title = styled.h1`
  color: red;
  font-size: 50px;
`

export default () => <Title>My page</Title>

Globalなスタイル

createGlobalStyleや、ThemeProviderなど全体に関わるものは_app.jsに書くことでページ全体に適用されます。

pages/_app.js
import React from "react";
import App, { Container } from "next/app";
import { createGlobalStyle, ThemeProvider } from "styled-components";

const GlobalStyle = createGlobalStyle`
  body {
    background: ${p => p.theme.backgroundColor};
    color: red;
  }
`;

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return (
      <ThemeProvider theme={{ backgroundColor: "yellow" }}>
        <Container>
          <GlobalStyle />
          <Component {...pageProps} />
        </Container>
      </ThemeProvider>
    );
  }
}

emotion

shell
npm i --save @emotion/core @emotion/styled babel-plugin-emotion
.babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    ["emotion"]
  ]
}

emotionはv10からno configでSSR対応までしてくれていますので以上で設定が完了です。
スタイルを当てた例が以下になります。

pages/index.js
/** @jsx jsx */
import { jsx, css } from "@emotion/core";
import styled from "@emotion/styled";

const style = css`
  color: hotpink;
`;

const StyledParagraph = styled.p`
  color: red;
  font-size: 40px;
`;

export default () => (
  <div css={style}>
    Welcome to
    <StyledParagraph>Next.js!</StyledParagraph>
  </div>
);

Globalなスタイル

例に漏れず、pages/_app.jsにGlobalコンポーネントを入れることで、ページ全体に適用されるスタイルが書けます。

pages/_app.js
import React from "react";
import App, { Container } from "next/app";
import { css, Global } from "@emotion/core";

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;

    return (
      <Container>
        <Global
          styles={css`
            body {
              background: green;
            }
          `}
        />
        <Component {...pageProps} />
      </Container>
    );
  }
}

おわりに

CSSは、たかがCSSされどCSSだと思います。プロジェクトに合ったCSSフレームワークを選んでいきましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away