Next.jsとemotionを使ってSSR時にCSSを適用しておく

2 min read読了の目安(約2600字

先日Beyond Magazineというメディアをオープンしました。
Next.js, Shifter での Headless CMS という構成で開発しましたが、いくつか知見を得たのでシェアしておきます。
(アーキテクチャなどは別途紹介する予定です)

構成

  • Next.js 10.2.0
  • Emotion 11.1.x

現象

いくつかの理由で SSR をしているのですが、SSR した場合に初期表示が崩れることがありました。
@emotion/cssを使用していましたが、この場合 Next.js の pre-render ページにスタイルが含まれない仕様のためです。Chrome の Inspector で Network のタブを開き該当リクエストを確認してみます。
Response の HTML に emotion のスタイルが入っていないことと、Preview では表示が崩れていることを確認できます。
(別の問題ですが svg も 404 表示されています。これは後から気づきました)。

import { css } from "@emotion/css";
import Image from "next/image";
export const Header = () => {
  return (
    <header
      className={css`
        width: 100%;
        padding: 16px 0;
        text-align: center;
        border-bottom: 1px solid rgba(0, 118, 255, 0.9);
      `}
    >
      <Image src="/nextjs.svg" alt="Next.js Logo" height={"32"} width={"64"} />
    </header>
  );
};

解決

emotion で SSR する際には使える API に制限があり、@emotion/react, @emotion/styledを使う必要がありました
(一番上に書いてあるのに気づかず長いことハマりました)

@emotion/reactは pragma を書いたり、書きたくない場合は runtime に細工する必要があり、更に emotion11 だとバージョン間で挙動が怪しい部分もあり、Issue が複数上がっていたことから、@emotion/styledを利用することにしました。
svg はbabel-plugin-inline-react-svgを利用すると pre-render 時に表示できるようになります。

import styled from '@emotion/styled'
import Nextjs from '../components/icons/nextjs.svg'
export const Header = () => {
  return (
    <StyledHeader>
      <Nextjs />
    </StyledHeader>
  )
}

const StyledHeader = styled.header`
  width: 100%;
  padding: 16px 0;
  text-align: center;
  border-bottom: 1px solid rgba(0, 118, 255, 0.9);

まとめ

実際のプロダクトでは、TOP ページにいきなりカルーセルがあるので、ファーストビューでテキストが画面の左に寄っていたり、画像が一瞬画面いっぱいに表示されてしまう現象が起こっていて UX がとても下がる状態でしたが、わがままを言っいくつかフィーチャーを後回しにしローンチ前に解決しました(焦りましたw)。
Beyond Magazine では雑誌を作ってきたメンバーを中心に高いデザイン性を目指しています。特に画像のクリエイティブのレベルが高いので、Web の技術者がその世界観を更に実現していけるように改善していく予定です。
今後はメディア単体でのマネタイズだけでなく質の高いコンテンツと多角的な事業で拡大していくのでご興味のある方は Twitter で DM ください!

参考