はじめに
これまでVuejsで開発をしてきたのですが、新しいプロジェクトでReactを利用することになりました。
キャッチアップのために、CSS in JS の代表的なライブラリの1つであるstyled-components
を利用して簡単なWebサイトを作成しました。
実装を進める中で自分が調査して、なるほどと思ったポイントをまとめました。
[作成したサイト]
[Github]
CSS in JS について
Reactのスタイリングの方法について
CSS in JS について説明する前にまず基本として、Reactでは要素をどのようにスタイリングするかについて記載します。
Reactでは、要素のスタイリングに下記の2つの方法を用意しています。
- 通常のHTMLでスタイリングをするようにcssファイルを別に指定して、classNameプロパティに指定する
- Reactエレメントのstyleプロパティにcssに解釈できるオブジェクトを渡して、スタイリングを行う
React公式では、パフォーマンスの観点から1の方法を推奨しています。
この公式の見解をそのまま当てはめるとcssファイルでスタイリングは定義したほうがいいとなります。
ただしCSSはグローバルスコープなので、比較的大きめのアプリケーションを実装する場合には名前の衝突やスタイルの影響範囲などをどのように担保するかの解決方法を検討する必要があります。
CSS in JS をなぜ利用するか
CSS in JS は、cssの定義をjsファイルに記載して、上記の2のようにインラインスタイルで定義することでcssのスコープをそのコンポーネントに閉じようというコンセプトでスタイリングを行う手法です。
Facebookの@Vjeuxさんの「React: CSS in JS」 でこの手法が有名になったようで現在では様々なライブラリが実装されています。
CSS in JS を採用する理由は、主に下記です。
- cssのスコープを対象のコンポーネントに閉じることでスタイル崩れを心配することなく開発したい
- JSXで記載したHTMLの構造とcssのスタイルを一緒に管理できる
- JSの既存のツールを利用して、静的解析やコードの入力補完がしやすい
[CSS in JS の各ライブラリ]
2021年 CSS in JS のライブラリはかなり色々と出ています。
- Emotion
- styled-components
- JSS
- glamorous
- Linaria
- CSS Modules
などがざっと調べただけでも出てきます。
npm trend を確認すると、emotion が一番人気ですが、jss / styled-components もかなり近いレベルで採用されているようです。
最初はemotionを利用してみようと思ったのですが、css記法を利用する際には JSX Pragmaの対応が必要とのことでまずはReactに慣れることを優先して簡単に導入できるstyled-componentsを採用しました。
(v4以降ではstyled-componentsでもcss propに対応したため、css propを利用したいという場合でもstyled-componentsで対応できるようになりました。)
styled-componentsでできること
styled-componentsはReactコンポーネントをCSSでスタイルすることが主な目的なのですが、下記のように様々なスタイリングのパターンに対応しています。
- コンポーネントに共通したテーマの設定(ThemeProviderを利用)
- 既存コンポーネントへのスタイルの適用(参考:Styling any component )
- 擬似クラス / 擬似要素の適用(参考:Pseudoelements, pseudoselectors, and nesting )
- React Nativeでの利用(参考: https://styled-components.com/docs/basics#react-native)
styled-componentsの使い方をざっくり知りたいという入門者向けの情報
まずは、ざっくりstyled-componentsの使い方が知りたいという方は下記のリンクが参考になります。
公式サイト:
https://styled-components.com/docs/basics#motivation
styled-componentsの使い方の初歩を丁寧に解説してくれている
https://www.webprofessional.jp/style-react-components-styled-components/
styled-componentsの基本的な使い方、Theme, Global
https://akabeko.me/blog/2020/11/styled-components/
比較的新しめのstyled-componentsの機能の紹介
個人的にこんなことまでできるんだと驚いた最後に比較的新しめの機能を3つ紹介させていただきます。
- ポリモーフィズムによるHTMLタグの動的な切り替え
- css prop への対応
- transient props
1. ポリモーフィズムによるHTMLタグの動的な切り替え
これまではstyled
の適用をする際にラップするHTML要素を決めなれけばならず、cssは同じだけどタグだけ変更したいとかpropsによって動的にHTMLの属性を変更したいというケースでは対応が難しい状態でした。
しかし、この機能を利用すれば、HTMLの属性をas propsによってrenderする際に決めることができます。
(乱用すると定義した場所でのタグの意味がなくなるので、注意は必要です。)
データによって表示を切り替える必要のある複雑なリストやグリッドを実装するときには重宝すると思います。
// as propsを利用した例:
import styled from "styled-components";
const Component = styled.div`
color: red;
`;
render(
<Component
as="button"
>
Hello World!
</Component>
)
上記のJSXは下記のようにbutton
タグに解釈されます。
// render されたHTML
<button class="sc-bqyKva ehfErK">Hello World!</button>
2. css prop への対応
以前はstyled-composentはcss prop
に対応できておらず、必ずスタイリング対象のHTMLタグが必要となっていたため無駄なDOMが構築されてしまうケースがありました。
CSS in JS系の有名なライブリとしてはemotionはcss prop
に対応できていたため、css prop
が利用したい場合はstyled-composentを採用せず、emotionを利用することが多かったようです。
css prop
に対応できたことで今後はstyled-composentが採用されることも増えるかと思います。
補足ですが、Typescriptで利用する場合には、下記の記載を追加しないと型エラーが出るようなのでご注意ください。
import {} from 'styled-components/cssprop'
3. transient props
styled-composentで既存コンポーネントのスタイリングをpropsを利用して行う場合に、propsが意図せず伝播してしまうことがありました。
少しわかりにくいので、例で説明します。
下記のように普通にスタイリングを行うと、Containerコンポーネントにのみprops.colorを適用させたい場合でもTextにまでprops.colorが伝播されてしまいます。
// 普通にスタイリングした場合
import styled from "styled-components";
const Text = props => <p {...props} />;
const Container = styled(Text)`
color: ${p => p.color};
`;
// colorがTextにまで伝播してしまう
<Container color="blue">Hello world!</Container>
この例のように自作コンポーネントの場合は、注意して実装すれば問題ないのですが、ライブラリなどで実装されたコンポーネント(例えば react-routerのなど) は自分でpropsを設定できないので伝播させたくない場合に困ってしまいます。
このような場合には、対象のpropsに$
を利用することでpropsを伝播させなくするのがtransient props
の機能です。
// 普通にスタイリングした場合
import styled from "styled-components";
const Text = props => <p {...props} />;
const Container = styled(Text)`
color: ${p => p.$color};
`;
// colorはContainer
<Container $color="blue">Hello world!</Container>
transient props の詳細な仕様については下記の記事が詳しいので、気になる方はご参照ください。
参考リンク
- React公式: CSS とスタイルの使用
- CSS Modules 所感
- 食べログ フロントエンドエンジニアブログ:SassからCSS Modules、そしてstyled-componentsに乗り換えた話
- クックパッド開発者ブログ:レシピサービスのフロントエンドに CSS in JS を採用した話
- Emotion > css props > JSX Pragma
- styled-component > API Reference > "as" polymorphic prop
- styled-component > API Reference > Transient props
- Evan Jacobs: Introducing “transient” props
- Github:[styled-component] Support css prop