Zenn
💅

Runtime CSS in JSでスタイルを書くときは、動的なスタイリングを避けてパフォーマンスを落とさないようにしよう

2025/02/19に公開
2
2

はじめに

Runtime CSS in JS は、JavaScript の実行時にスタイルを適用する仕組みのためパフォーマンスに影響を及ぼすことがある。

想定環境

以下の CSS in JS ライブラリを想定。

他の Runtime CSS in JS ライブラリでも特に問題ないと思われる。

改善方法

propsを使った動的なスタイリングを使わない

props を利用してスタイルを動的に変更することは一般的だが、都度計算処理が入るためパフォーマンスに影響がある。

// https://emotion.sh/docs/styled#changing-based-on-props
const Button = styled.button`
  color: ${(props) => (props.primary ? "hotpink" : "turquoise")};
`;

render(
  <Container>
    <Button>This is a regular button.</Button>
    <Button primary>This is a primary button.</Button>
  </Container>
);

頻繁に状態が変化するコンポーネントでは、特に影響が大きくなる。

data属性を利用する

この方法ではdata 属性でスタイルを定義しており、ランタイムでのスタイル計算が不要となる。

const Button = styled.button`
  color: turquoise;

  &[data-primary="true"] {
    color: hotpink;
  }
`;

render(
  <Container>
    <Button>This is a regular button.</Button>
    <Button data-primary={primary}>This is a primary button.</Button>
  </Container>
);

propsでの指定だと分岐が多く状態が分かりづらくなる。data属性で状態をひとまとまりにする事で状態の可読性も上げることができる。

CSS Custom Properties(変数)を利用する

対象のコンポーネントに対して、インラインスタイルでCSS Custom Propertiesを定義して利用する方法もある。

const Button = styled.button`
  color: var(--color);
`;

render(
  <Container>
    <Button
      style={{
        "--color": primary ? "hotpink" : "turquoise",
      }}
    >
      This is a regular button.
    </Button>
    <Button
      style={{
        "--color": primary ? "hotpink" : "turquoise",
      }}
    >
      This is a primary button.
    </Button>
  </Container>
);

ただし、同コンポーネント同士がネストされた場合に変数の値が意図しない上書きをされてしまう場合は、同じコンポーネントをネストしない等のような制限は必要かもしれない。

デフォルト値を指定しておく場合

var()は、第一引数が定義されていない場合は第二引数に指定した代替値が適用される。

const Button = styled.button`
  color: var(--color, turquoise);
`;

render(
  <Container>
    <Button>
      This is a regular button.
    </Button>
    <Button
      style={{
        "--color": primary ? "hotpink" : "turquoise",
      }}
    >
      This is a primary button.
    </Button>
  </Container>
);

デフォルト値をコンポーネント側に持たせない場合はvar()に指定しても良いかもしれない。

attr()を利用する⚠

CSS Level 5 でattr()で取得できる値が<string>以外も扱えるようになった。執筆時点ではChrome 133でしか利用が出来ないが、今後利用範囲が広がるとpropsで渡さずともCSSのattr()で完結できるケースが増えていくと思われる。

const Button = styled.button`
  width: attr(data-size px);
`;

render(
  <Button data-width="100px">This is a regular button.</Button>
);

https://developer.mozilla.org/ja/docs/Web/CSS/attr
https://developer.chrome.com/blog/advanced-attr?hl=ja
https://caniuse.com/css3-attr

Interpolated Selector(動的セレクタ)を使わない

以下のように、styled コンポーネントのセレクタ内で他のコンポーネントを参照する記述(${Child})を「Interpolated Selector」などと呼ぶようである(各ライブラリのissueなどでそう表現されている)。
この方法だと先述と同様にランタイムでセレクタを解決するため、その分パフォーマンスが低下する。

// https://emotion.sh/docs/styled#targeting-another-emotion-component
const Child = styled.div`
  color: red;
`;

const Parent = styled.div`
  ${Child} {
    color: green;
  }
`;

render(
  <div>
    <Parent>
      <Child>Green because I am inside a Parent</Child>
    </Parent>
    <Child>Red because I am not inside a Parent</Child>
  </div>
);

静的なCSSセレクタを利用する

styledなコンポーネントに対してのスタイル定義ではなくなるが、CSSのネイティブな:has() セレクタなどを活用し、JS の処理を介さずにスタイルを適用することでパフォーマンスを向上できる。

const Child = styled.div`
  color: red;
`;

const Parent = styled.div`
  & > * {
    color: green;
  }
`;

他にも同じようなケースで対象のコンポーネントの存在チェックなどを行うこともある。

const Parent = styled.div<{ hasChild }>`
  ${(props) => props.hasChild && "margin-top: 1rem"};
`;

render(<Parent hasChild={!!Text}>{<Child>{Text}</Child>}</Parent>);

こういった場合もJSで計算したりpropsを利用するのではなくCSSのネイティブなスタイルの利用も有用である。

const Parent = styled.div`
  &:has(> *) {
    margin-top: 1rem;
  }
`;

https://zenn.dev/zozotech/articles/5311d500ad7d74

グローバルスタイルをCSS in JS経由で定義しない

styled-componentsであればcreateGlobalStyle、EmotionであればinjectGlobalもしくはGlobalを利用してグローバルスタイルを定義できる。
CSS in JSの機能にまとめておくと見通しが良いケースもあるが、いずれにしてもこれらの機能を介してスタイルを動的に変更することはできないため、CSS in JSを利用するメリットはほぼない。

グローバルなスタイル(リセットCSSなど)は、CSSファイルに定義してグローバルに読み込む方が良い。JSを介するオーバーヘッドの問題やファイルに対してキャッシュが効くことでの読み込み速度の改善など恩恵などが得られるケースがある。

まとめ

Runtime CSS in JS は、直感的にスタイルを記述することが出来るため柔軟な実装が可能である。しかし、それとはトレードオフでパフォーマンスへ影響を及ぼすような書き方が積み重なってしまうケースがある。

デザインやスタイルを注視して、動的な指定でなくとも良いケースは従来のCSSの定義を思い出して実装を行うと良い。

2
株式会社ZOZO

Discussion

junerjuner

実装によっては css 構文上の 変数部分を Custom Properties にしたり する実装もあるみたいで面白いですね。

hirohiro

Zero-Runtime CSS in JSはCustom Propertiesを駆使してRuntimeに近い動きにしてますね!

ログインするとコメントできます