Next.js アップデートに伴い、Emotion からの脱却。— Chakra UIからTailwind CSSへの移行

こんにちは! 株式会社ケップルで Product Engineer をしている吉田です。

2024年10月に、スタートアップメディア「KEPPLE」にて、
Chakra UI から Tailwind CSSRadix UI へ移行しました。

Next.js を採用している企業の中には、Emotion を基盤とする UI ライブラリとの相性問題から、移行を検討している方も多いのではないでしょうか。
今回の記事では、私たちが直面した課題や移行に至った背景、具体的なプロセスや得られた知見を共有したいと思います。

 

背景:なぜ Chakra UI から Tailwind CSS へ移行したのか

スタートアップメディア「KEPPLE」ではデザイン刷新の計画があり、コンポーネントの再構築が必要でした。そのため、新たな UI ライブラリへ移行するちょうどいい機会でした。

Chakra UI が抱えていた技術的課題

  1. Next.js との整合性問題
    Chakra UIは、内部的にEmotionを用いたCSS-in-JS手法によってスタイリングを行っています。
    しかし、Next.jsでServer-Side Rendering(SSR)や新たなReact Server Components(RSC)を活用する際、この動的なスタイル生成が問題を引き起こすことがありました。

    たとえば、ビルド時に静的に生成されたCSSではなく、レンダリングサイクルごとに異なるクラス名やスタイルが生成される結果、App Router移行時のRSCとの組み合わせで期待通りのスタイルが適用されないケースが生じます。これにより、SSR時にDOMとCSSの不整合が発生し、表示崩れやコンポーネントの初期描画時のスタイル不備を誘発する可能性がありました。

    このような問題から、Next.jsのpages RouterからApp Routerへの移行を円滑に進められませんでした。

  2. パフォーマンスへの懸念
    UI ライブラリを使用すると、バンドルサイズが少なからず大きくなります。
    そのため、移行することでバンドルサイズを小さくして、パフォーマンス改善できるのではないかという期待もありました。

 

なぜ Tailwind CSS を選んだのか

移行先としてTailwind CSSを選んだ理由は、以下の点にありました。

  1. チーム内の知見が豊富だったこと
    Tailwind CSSは、過去のプロジェクトで使用した経験があり、導入や学習コストを抑えられる見込みがありました。

  2. 活発なコミュニティと技術トレンド
    Tailwind CSSはとても勢いのあるCSSフレームワークであり、コミュニティが活発です。公式のサポートが充実しているほか、技術ブログやフォーラムを通じてキャッチアップがしやすいため、将来的な拡張性にも期待できました。

  3. プロダクトの性質的にもヘッドレス系のUI ライブラリで十分だったこと
    KEPPLEは、過剰なデザインや複雑なUIパターンを必須としない性質から、シンプルかつ柔軟な実装が求められていました。
    そのため、基本的な部分はTailwind CSSで自由度の高いスタイリングを行い、より複雑なコンポーネントが必要な箇所には、ヘッドレス系UIライブラリであるRadix UIのPrimitivesコンポーネントを採用しました。

これにより、Chakra UIのようなリッチなスタイリング手法ではなく、プロダクトに合わせたミニマルかつ自在なデザイン表現を実現することを可能にしました。

移行プロセス:どのように進めたか

UIライブラリの移行は計画的に実施していかなければいけません。
その中で弊社では、どのようなプロセスで、どれくらいの期間を要して、実施したかを紹介します。

段階的な移行手順

Tailwind CSSへの移行は、以下の手順で進めました。

  1. 既存コンポーネントの洗い出し
    現在使用している全コンポーネントをリストアップし、それぞれがプロジェクト内で必要かどうかを精査しました。 これにより、不要なコンポーネントを削減し、効率的な移行計画を立てられました。

  2. 再設計と分割
    洗い出されたコンポーネントを Common (再利用可能なコンポーネント) と Feature (ドメインに依存したコンポーネント) ごとに分類し、再設計を行いました。
    また、既存の修正にも対応できるように、Tailwind CSS 移行用のブランチを別途作成し、そのブランチで移行作業を進めました。

  3. Storybookの活用
    コンポーネント単位での動作確認を効率的に行うため、Storybookを導入しました。 これにより、各コンポーネントが意図通りに機能しているかを視覚的にチェックできるようにして、ステークホルダーへ事前に確認をしてもらいました。
    また、VRTやスナップショットテストも、移行後に対応する予定です。

  4. ページへの適用
    再設計後のコンポーネントをページへ統合し、レイアウトやスタイルの一貫性を確保しました。

  5. Chakra UI の除去
    不要となった Chakra UI 関連の依存やディレクトリを整理しました。

移行期間と作業量

結果として、60以上のコンポーネントと17ページを約2〜3ヶ月で移行しました。
1日あたり1〜2個以上のコンポーネントを移行するペースです。

 

移行に伴う工夫

移行中には、以下のような取り組みを行いました。

  1. 週次で振り返りを実施
    スクラム的な手法を一部取り入れ、それぞれのチケットにストーリーポイントを振り分け、週1のスプリントで、進捗をチームで共有・改善しました。 
    • ストーリーポイントを設定することで、感覚的な見積もりではなく、定量的に見積もりをすることができたため、ステークホルダーに納期や進捗共有、テストの依頼などをスムーズに行うことができたこと。
    • 週ごとに、感じたこと・少しでも気になっていることを話し合って、合意形成をとりながら進めることで後戻りの少ない開発ができたこと。

  2. ルール整備と統一
    開発チーム内で使用するスタイルやコーディング規約を言語化し、Github Repository の Wiki で共有することで、メンバー間での認識を統一し、開発効率を向上させました。

  3. スタイリングが無法地帯にならないように整備
    Chakra UI 時代は Props ベースで柔軟にスタイル指定が可能でしたが、それは統一感やメンテナンス性を損なう原因にもなりました。Tailwind CSS 移行後は、クラスの動的付与は最小限にし、必要な変更点を Props で限定的に行うようにしました。

    例えば、Spinner のコンポーネントでは、動的にサイズを変更したい要件がありましたが、 PropsでclassName をそのまま渡せるようにするのではなく、sizeを指定したPropsしか渡せないようにしています。

    
    export const Spinner = ({ size = 'md' }: Props): JSX.Element => {
      const sizeClasses = {
        ['size-6']: size === 'sm',
        ['size-10']: size === 'md',
      };
      const containerClasses = clsx('flex justify-center', sizeClasses);
    
      return (
        <div aria-label='loading'>
          <SpinnerIcon className={spinnerClasses} />
        </div>
      );
    };
      
    また、Tailwind CSSでは、ビルド時に使用されるクラスを静的に解析し、未使用のスタイルを削除します。そのため、size-${props.size} のように動的な指定ができないので、clsxを使用して、渡すようにしましょう。

    tailwindcss.com

 

移行で明らかになった課題

一方で、移行に伴い新たな課題も浮き彫りになりました。

  1. 可読性の低下
    Tailwind CSS はクラスベースの指定ゆえ、複雑なスタイルになるとクラスが羅列され可読性が低下します。一時的に、clsx を用いてグルーピングするなどの工夫で対処しました。
    
    className={clsx(
        'h-full w-full rounded p-2',
        'hover:bg-orange-50 hover:rounded hover:border hover:border-orange-400',
        'focus:bg-orange-50 focus:rounded focus:border focus:border-orange-400',
        'active:bg-orange-100 active:rounded active:border active:border-orange-500',
      )}
      
    
  2. 依存関係のバージョンアップ
    移行中に関連ライブラリのバージョンアップが必要になるケースもあり、事前の依存関係整理が重要であると実感しました。

移行から得た学び

Chakra UIからTailwind CSSへの移行を通じて、以下の学びが得られました。

  1. 技術トレンドの移り変わり
    Emotionのように一時期広く採用されていた技術でも、数年で別の選択肢に取って代わられることがあります。 今回の移行は、技術の選定において長期的な視点と柔軟性が求められることを改めて感じました。現在の技術が必ずしも将来の標準になるとは限らないため、継続的にキャッチアップすることが重要です。

  2. ルール整備の重要性
    コンポーネント設計やスタイリングのルールを言語化してチームで共有することで、 認識のずれを防ぎ、効率的な開発が可能となりました。 この取り組みは、移行作業に限らず今後のプロジェクト運用にも役立ちそうなので、意識して取り組んでいきます。

  3. 移行はスキルアップの好機
    新技術導入は単なる差し替え以上の価値があり、チームのスキルアップやプロセス改善、リファクタリングなど、どうあるべきなのかをじっくりと考えられるきっかけになりました。

さいごに

今回の移行プロジェクトは、単なるフレームワークの切り替えに留まらず、多くの学びを得ることができ、とてもポジティブに感じています。

今後も技術負債の返済は積極的に行いながら、よりよいプロダクトに進化していけるように努めていきたいと思います!